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

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

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

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

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

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

注意: 我没有寻找在 DOM 中存储 JSON 的替代方案,我已经决定这是解决我所面临的特定问题的最佳方案。我只是在寻找最好的方法。

58698 次浏览

一般来说,我会尝试使用 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'));

我建议将 JSON 放入带有函数回调(类似于 JSONP)的内联脚本中:

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

如果执行的脚本是在文档之后加载的,那么您可以将其存储在某个地方,可能还有一个额外的标识符参数: someCallback("stuff", { ... });

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

我认为你最初的方法是最好的,HTML5规范甚至解决了这个问题:

”当用于包含数据块(与脚本相反)时,数据 必须嵌入内联,数据的格式必须使用 属性,则不能指定 src 属性,并且 脚本元素的内容必须符合要求 定义为所使用的格式。”

阅读此处: http://dev.w3.org/html5/spec/Overview.html#the-script-element

你就是这么做的。有什么理由不去爱呢?属性数据没有必要的字符编码。如果需要,可以格式化它。它的表现力和预期的用途是明确的。它看起来不像一个黑客(例如,使用 CSS 来隐藏你的“载体”元素)。完全有效。

这种在脚本标记中嵌入 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>

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

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

假设您想在 HTML 中包含这个 JSON:

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

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

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

现在您可以通过使用 JavaScript 读取元素的 textContent并解析它来访问它:

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

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攻击就像其他答案所暗示的那样。