【问题标题】:Best practice for embedding arbitrary JSON in the DOM?在 DOM 中嵌入任意 JSON 的最佳实践?
【发布时间】:2012-02-16 22:58:24
【问题描述】:

我正在考虑像这样在 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);

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

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

【问题讨论】:

  • 你为什么不把它作为 javascript 中的var
  • @Krizz,它需要成为静态文档的一部分,然后由复杂的封装 javascript 链处理。将它存储在 DOM 中是我想要做的。
  • @Krizz 我遇到了类似的问题。我想在不执行 AJAX 请求的情况下将每个用户的数据放在不同的站点中。所以我在一个容器中嵌入了一些 PHP,做了一些类似于你上面的操作来获取 javascript 中的数据。
  • 我认为您的原始方法实际上是最好的。它在 HTML5 中 100% 有效,富有表现力,不会创建“假”元素,您只需使用 CSS 删除或隐藏这些元素;它不需要任何字符编码。缺点是什么?
  • 如果你的 JSON 对象中有一个值为 &lt;/script&gt;&lt;script&gt;alert()&lt;/script&gt;&lt;script&gt; 的字符串,你会得到惊喜。除非您先清理数据,否则这是不安全的。

标签: javascript json dom embedding decoupling


【解决方案1】:

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

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

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

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

【讨论】:

  • 谢谢。规范中的引述让我信服。
  • 只有在您首先检查和清理 JSON 对象时才完全有效:您不能只嵌入用户原始数据。请参阅我对这个问题的评论。
  • 特别想知道:放它的好地方是什么?头部还是身体,顶部还是底部?
  • 不幸的是,appears CSP 策略可能/将停止所有 script 标签。
  • 如何有效防止嵌入包含 的 JSON,从而允许 HTML 注入?是否有一些可靠/简单的东西,还是使用数据属性更好?
【解决方案2】:

作为一个大方向,我会尝试改用HTML5 data attributes。没有什么可以阻止您输入有效的 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.parse 将不起作用(至少本机 Google Chrome JSON.parse 不会)。 JSON 规范需要双引号。但这很容易使用像...&amp;lt;unicorns&amp;gt;:... 这样的实体来解决。
  • 不过有一个问题:HTML 5 中的属性长度有限制吗?
  • 是的,可以。您也可以切换它,以便您的 HTML 使用单引号,而 JSON 数据使用双引号。
  • 好的,找到了我的问题的答案:stackoverflow.com/questions/1496096/…——这对我的目的来说已经足够了。
  • 这不适用于单个字符串,例如"I am valid JSON" 并在标签中使用双引号,或在字符串中使用单引号和单引号,例如data-unicorns='"My JSON's string"' 作为单引号不会使用 JSON 编码转义。
【解决方案3】:

这种在脚本标签中嵌入 json 的方法存在潜在的安全问题。假设 json 数据来自用户输入,则可以制作一个数据成员,该成员实际上会脱离脚本标签并允许直接注入 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>

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

【讨论】:

  • 这是真的,但这并不是该方法的真正安全漏洞。如果您曾经将源自用户输入的内容放入您的页面,您必须努力避免它。只要您对用户输入采取正常的预防措施,此方法仍然有效。
  • JSON 不是 HTML 的一部分,HTML 解析器只是继续运行。这与 JSON 成为文本段落或 div 元素的一部分时相同。 HTML 转义程序中的内容。此外,您还可以转义斜线。虽然 JSON 不需要这样做,但它确实可以容忍不需要的斜线。可以使用她来确保嵌入的安全。 PHP 的 json_encode 默认会这样做。
【解决方案4】:

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

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

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

在 HTML 中创建一个隐藏的&lt;div&gt;。接下来,通过编码不安全的实体(例如 &、、"、' 和 /)来转义 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>"}

【讨论】:

  • 我相信这是最好和最安全的答案。请注意,许多常见的 JSON 字符会被转义,某些字符会被双重转义,例如对象 {name: 'Dwayne "The Rock" Johnson'} 中的内引号。但最好还是使用这种方法,因为您的框架/模板库可能已经包含了一种安全的 HTML 编码方式。另一种方法是使用 base64,它既是 HTML 安全的,又可以安全地放入 JS 字符串中。使用 btoa()/atob() 在 JS 中进行编码/解码很容易,而且你可能很容易做服务器端。
  • 更安全的方法是使用语义正确的&lt;data&gt; element 并将JSON 数据包含在value 属性中。然后,如果使用双引号将数据括起来,则只需使用 &amp;quot 转义引号,如果使用单引号,则只需使用 &amp;#39; 转义(这可能更好)。
【解决方案5】:

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

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

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

【讨论】:

  • @BenLee 它应该工作得很好,唯一的缺点是必须定义回调函数。如果您的 JSON 中有特殊 HTML 字符(例如 &)和引号,则另一个建议的解决方案会中断这些字符。
  • 这样感觉更好,因为你不需要 dom 查询来查找数据
  • @copy 这个解决方案仍然需要转义(只是另一种),请参阅 MadCoder 的回答。为了完整起见,将其留在这里。
  • 这似乎会更快,因为对象被直接解析为 JavaScript,而不是从 DOM 获取文本并通过 JSON.parse。在我的网站上,我使用const MY_DATA = ...
【解决方案6】:

HTML5 包含一个&lt;data&gt; element,用于保存机器可读数据。作为&lt;script type="application/json"&gt; 的替代方案(也许更安全),您可以在该元素的value 属性中包含您的JSON 数据。

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>

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

【讨论】:

    【解决方案7】:

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

    【讨论】:

    • 您不要将 CSS 和 Javascript 内联在网页中,因为它们通常在其他页面之间共享。如果有问题的数据是由服务器为该上下文显式生成的,那么嵌入它比对无法缓存的内容发起另一个请求要高效得多。
    • 这是因为我正在对设计不佳的遗留系统进行更新,而不是重新设计整个系统,我只需要修复一个部分。将 JSON 存储在 DOM 中是解决这一问题的最佳方法。另外,我同意@jamietre 所说的。
    • @jamietre 请注意,OP 声明此 JSON 字符串仅在 稍后 需要。问题是它是否总是需要,或者仅在某些情况下需要。如果仅在某些情况下需要它,那么将它放在外部文件中并仅有条件地加载它是有意义的。
    • 我同意有许多“假设”可以以某种方式倾斜规模。但一般来说,如果您知道页面何时呈现您将需要的东西——即使只是可能——通常最好立即发送它。就像,如果我有一些开始折叠的信息框,我通常希望将它们的内容包含在内,以便它们立即展开。与现有请求上的一些额外数据的开销相比,新请求的开销要大得多,并且它创建了响应速度更快的用户体验。我确定有一个断点。
    • 也许这应该被删除?它没有回答这个问题。 OP 并没有询问“我如何从服务器获取数据到客户端”的一般问题,如果他们有,这个问题可能会因为过于宽泛而被删除。
    猜你喜欢
    • 2011-11-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多