在DOM中嵌入任意JSON的最佳实践?


110

我正在考虑像这样在DOM中嵌入任意JSON:

<script type="application/json" id="stuff">
    {
        "unicorns": "awesome",
        "abc": [1, 2, 3]
    }
</script>

这类似于将任意HTML模板存储在DOM中以供以后与JavaScript模板引擎一起使用的方式。在这种情况下,我们以后可以检索JSON并使用以下方法进行解析:

var stuff = JSON.parse(document.getElementById('stuff').innerHTML);

这有效,但这是最好的方法吗?这是否违反任何最佳做法或标准?

注意:我不是在寻找在DOM中存储JSON的替代方法,我已经决定这是解决我遇到的特定问题的最佳解决方案。我只是在寻找最佳方法。


1
您为什么不将其作为varJavaScript?
Krizz 2012年

@Krizz,它必须是静态文档的一部分,稍后由复杂的封装javascript链进行处理。我想做的就是将其存储在DOM中。
李李

@Krizz我遇到类似问题。我想将数据放置在每个用户都不同的站点中,而无需执行AJAX请求。因此,我将一些PHP嵌入容器中,以实现与javascript相似的操作。
帕特里克·洛里奥

2
我认为您的原始方法实际上是最好的。它在HTML5中是100%有效的,具有表现力,它不会创建“假”元素,而您只能使用CSS删除或隐藏这些元素;而且不需要任何字符编码。不利之处是什么?
Jamie Treworgy 2012年

22
如果您</script><script>alert()</script><script>的JSON对象中有一个带有值的字符串,那么您会感到惊讶。除非您先清理数据,否则这是不安全的。
silviot 2014年

Answers:


77

我认为您的原始方法是最好的。HTML5规范甚至解决了这种用法:

“当用于包含数据块(相对于脚本)时,必须以嵌入式方式嵌入数据,必须使用type属性指定数据格式,不得指定src属性,并且必须包含script元素的内容符合为使用的格式定义的要求。”

在这里阅读:http : //dev.w3.org/html5/spec/Overview.html#the-script-element

您已经做到了。什么不是爱?属性数据无需根据需要进行字符编码。您可以根据需要对其进行格式化。它表现力强,预期用途明确。感觉不像是黑客(例如,使用CSS隐藏“ carrier”元素)。这是完全有效的。


3
谢谢。规范中的报价使我信服。
李李

17
仅当您首先检查并清理JSON对象时,它才完全有效:您不能仅嵌入用户原始数据。看到我对这个问题的评论。
silviot 2014年

1
额外的疑惑:放置它的好地方是什么?头还是身体,顶部还是底部?
短讯

1
不幸的是,似乎 CSP策略可能/将停止所有script标签。
拉里K

2
您如何有效地防止嵌入包含</ script>并因此允许HTML注入的JSON?有什么扎实/容易的东西,还是使用数据属性更好?
jonasfj

23

作为一般方向,我将尝试使用HTML5数据属性。没有什么可以阻止您输入有效的JSON。例如:

<div id="mydiv" data-unicorns='{"unicorns":"awesome", "abc":[1,2,3]}' class="hidden"></div>

如果您使用的是jQuery,那么检索它就很简单:

var stuff = JSON.parse($('#mydiv').attr('data-unicorns'));

1
说得通。不过请注意,键名用单引号JSON.parse将不起作用(至少本地Google Chrome JSON.parse无效)。JSON规范要求双引号。但这很容易使用来修复...&lt;unicorns&gt;:...
李·李

4
但是,有一个问题:HTML 5中的属性长度是否有限制?
李·李

是的,那行得通。您也可以切换它,以便您的HTML使用单引号,而JSON数据使用双引号。
Horatio Alderaan 2012年

1
好的,找到我的问题的答案:stackoverflow.com/questions/1496096/…-这足以满足我的目的。
李·李

2
这不适用于单个字符串,例如"I am valid JSON",对标签使用双引号,或者对字符串中的单引号使用单引号,例如data-unicorns='"My JSON's string"',对于编码为JSON的单引号不会进行转义。
罗比·阿弗里尔

13

这种将json嵌入脚本标签中的方法存在潜在的安全问题。假设json数据源自用户输入,则有可能制作一个数据成员,该成员实际上将脱离script标签并允许直接注入dom。看这里:

http://jsfiddle.net/YmhZv/1/

这是注射

<script type="application/json" id="stuff">
{
    "unicorns": "awesome",
    "abc": [1, 2, 3],
    "badentry": "blah </script><div id='baddiv'>I should not exist.</div><script type="application/json" id='stuff'> ",
}
</script>

转义/编码只是没有办法。


7
的确如此,但这并不是该方法的安全缺陷。如果您曾经将源自用户输入的内容放入页面中,则必须谨慎地进行转义。只要您采取有关用户输入的常规预防措施,此方法仍然有效。
Ben Lee

JSON不是HTML的一部分,HTML解析器一直在发展。这与JSON成为文本段落或div-element的一部分时相同。HTML转义程序中的内容。此外,您还可以转义斜线。尽管JSON不需要这样做,但它确实容忍了不必要的斜杠。可以使用它来使其安全嵌入。PHP的json_encode默认情况下会执行此操作。
蒂莫·蒂霍夫

7

请参阅OWASP的XSS预防备忘单中的规则#3.1

假设您要在HTML中包含此JSON:

{
    "html": "<script>alert(\"XSS!\");</script>"
}

<div>在HTML中创建隐藏的内容。接下来,通过对不安全的实体(例如&,<,>,“,”,“和//)进行编码来转义JSON,并将其放在元素中。

<div id="init_data" style="display:none">
        {&#34;html&#34;:&#34;&lt;script&gt;alert(\&#34;XSS!\&#34;);&lt;/script&gt;&#34;}
</div>

现在,您可以通过textContent使用JavaScript 读取元素并对其进行解析来访问它:

var text = document.querySelector('#init_data').textContent;
var json = JSON.parse(text);
console.log(json); // {html: "<script>alert("XSS!");</script>"}

我相信这是最好和最安全的答案。请注意,许多常见的JSON字符都会被转义,某些字符会被双重转义,例如object中的内引号{name: 'Dwayne "The Rock" Johnson'}。但是,最好还是使用这种方法,因为您的框架/模板库可能已经包含执行HTML编码的安全方法。另一种选择是使用base64,它既安全又安全,可以放在JS字符串中。使用btoa()/ atob()在JS中进行编码/解码很容易,并且在服务器端也可能很容易。
sstur

甚至更安全的方法是使用语义正确的<data>元素,并将JSON数据包括在value属性中。然后,&quot如果使用双引号将数据括起来,或者&#39;如果使用单引号(可能会更好),则只需要使用来转义引号。
朗尔·伯格

5

我建议将JSON放入具有函数回调(有点像JSONP)的内联脚本中:

<script>
someCallback({
    "unicorns": "awesome",
    "abc": [1, 2, 3]
});
</script>

如果将执行脚本加载到文档之后,则可以将其存储在某个位置,并可能带有附加的标识符参数: someCallback("stuff", { ... });


@BenLee它应该可以很好地工作,唯一的缺点是必须定义回调函数。如果您的JSON中有特殊的HTML字符(例如&)和引号,则建议使用其他解决方案。
复制

感觉更好,因为您不需要dom查询来查找数据
Jaseem

@copy此解决方案仍然需要转义(只是另一种),请参阅MadCoder的答案。只是为了完整性而保留在这里。
pvgoran

2

我的建议是将JSON数据保留在外部.json文件中,然后通过Ajax检索这些文件。您没有将CSS和JavaScript代码放在网页上(内联),那么为什么要使用JSON?


12
您不要将CSS和Javascript内联到网页中,因为它们通常在其他页面之间共享。如果所讨论的数据是由服务器为此上下文显式生成的,则嵌入该数据要比对无法缓存的内容发起另一个请求要有效得多。
Jamie Treworgy 2012年

这是因为我要对一个设计不佳的旧系统进行更新,而不是重新设计整个系统,我只需要修复一部分即可。在DOM中存储JSON是修复此部分的最佳方法。另外,我也同意@jamietre所说的话。
李·李

@jamietre请注意,OP声明此JSON字符串仅在以后需要。问题是,是否始终需要它,或者仅在某些情况下才需要它。如果仅在某些情况下需要它,那么将其保存在外部文件中并仅有条件地加载它确实有意义。
森那维达斯

2
我同意,有很多“假设”可以以某种方式推翻比例。但是通常来说,如果您知道何时呈现页面,则将需要什么-即使只是可能的-通常最好立即发送。就像,如果我的信息框开始折叠,我通常希望将它们的内容内联,以便它们立即展开。与现有请求中的一些额外数据相比,新请求的开销要大得多,并且可以创建响应速度更快的用户体验。我肯定有一个转折点。
Jamie Treworgy 2012年

2

HTML5包含用于保留机器可读数据的<data>元素。作为(也许更安全)替代<script type="application/json">您可以将JSON数据包括在该value元素的属性中。

const jsonData = document.querySelector('.json-data');
const data = JSON.parse(jsonData.value);

console.log(data)
<data class="json-data" value='
  {
    "unicorns": "awesome",
    "abc": [1, 2, 3],
    "careful": "to escape &#39; quotes"
  }
'></data>

在这种情况下&#39;&quot;如果您选择用双引号将值引起来,则需要用或替换所有单引号。否则,建议您像其他答案一样冒险XSS攻击。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.