浏览器如何准确解析脚本标签?

Posted

技术标签:

【中文标题】浏览器如何准确解析脚本标签?【英文标题】:How do browsers parse a script tag exactly? 【发布时间】:2013-01-12 12:36:50 【问题描述】:

我刚刚遇到了一个关于 html 解析的病态案例。我一直认为<script> 标签会一直运行到第一个关闭</script> 标签。但事实证明,并非总是如此

这是有效的:

<script><!--
alert('<script></script>');
--></script>

即使这是有效的:

<script><!--
alert('<script></script>');
</script>

但这不是:

<script><!--
alert('</script>');
--></script>

这也不是:

<script>
alert('<script></script>');
</script>

这种行为在 Firefox 和 Chrome 中是一致的。因此,令人难以置信的是,浏览器似乎接受脚本标签内的 html 注释内的打开+关闭脚本标签。那么问题来了,浏览器really是如何解析脚本标签的呢? 这很重要,因为我使用的 HTML 解析库 Nokogiri 假定了明显的(但不正确的)直到第一个关闭标记规则并且没有处理这种边缘情况。我想大多数其他库也不会处理它。

【问题讨论】:

将您的脚本放在一个单独的文件中。 问题与html解析有关,也就是说我在处理别人的网页。 HTML 解析的边缘案例......我现在要哭着睡觉了,祝你好运:D @Daniel 是的,我的意思是通过将实际的 javascript 代码放在一个引用的 JS 文件中,将其从 HTML 解析中取出。 这是对浏览器应该使用的状态机的详细描述:whatwg.org/specs/web-apps/current-work/multipage/…。 TBH,我不确定它是否支持您所看到的。 【参考方案1】:

在仔细研究了given by Timand Jukka的链接后,我得到了以下答案:

在打开&lt;script&gt;标签之后,解析器进入data1状态 如果在 data1 状态下遇到&lt;!--,则切换到 data2 状态 如果在任何状态下遇到--&gt;,则切换到data1状态 如果在 data2 状态下遇到&lt;script[\s/&gt;],则切换到 data3 状态 如果在 data3 状态下遇到&lt;/script[\s/&gt;],则切换到 data2 状态 如果在任何其他状态下遇到&lt;/script[\s/&gt;],则停止解析

【讨论】:

换句话说,如果您要嵌入 JSON,则在将其字符串化后,您只需将所有 &lt; 替换为 \074,您就安全了吗?因为那只能出现在字符串中,他们会再次将该转义字符串读取为&lt; 呃,JSON不喜欢八进制,必须是unicode,所以\u003c 或用“”替换“”【参考方案2】:

根据 HTML 4.01 规范,所有示例都是无效的:script 的内容声明为CDATACDATA 的description 表示:

“尽管 STYLE 和 SCRIPT 元素使用 CDATA 作为其数据模型,但对于这些元素,用户代理必须以不同方式处理 CDATA。标记和实体必须被视为原始文本并按原样传递给应用程序。字符序列“&lt;/”(结束标记打开分隔符)的第一次出现被视为终止元素内容的结尾。在有效文档中,这将是元素的结束标记。”

正如您所观察到的,在某些情况下,浏览器可能不会强制执行此规则,而是识别成对的开始和结束标记。从规范的角度来看,这是对无效文档的处理,即错误处理。目前尚不清楚他们到底在做什么以及为什么。这似乎取决于&lt;!-- 的存在,这对HTML 4.01 解析应该没有任何影响(它不是CDATA 内容中的评论开启器)。

在 XHTML 中,应用部分不同的规则,因为在 XHTML 中,&lt;!--script 元素的内容中打开注释。

顺便说一句,由于script 中缺少type 属性,所有示例都是无效的HTML 4.01 和无效的XHTML。该属性不是必需的(浏览器默认将内容视为 JavaScript),但这些规范要求它。

在 HTML5 中,其他规则适用。它们相当复杂,它们应该描述浏览器的行为。除了强加restrictions on content(禁止例如&lt;!--而不匹配--&gt;),HTML5还指定parsing rules。

【讨论】:

【参考方案3】:

标签的内容仍然是 HTML,除非您将其标记为不是 HTML。在 HTML 中,&lt;word&gt; 被认为是一个标签,&amp;lt; 需要写成&amp;lt; 以避免这种行为。或者,您想让&lt;script&gt; 的内容成为文本节点;使用这个公式:

<script type="text/javascript">
//<![CDATA[
  // your code, with < and & and "", woohoo!
//]]>
</script>

&lt;![CDATA[ ... ]]&gt; 将文档的一部分描述为纯文本,没有标记。斜线是为了让 JavaScript 不会混淆;第一组斜线在 CDATA 之外,但它们是 HTML 安全的,所以没有问题。

编辑:刚刚意识到问题是关于解析,而不是编写 HTML。哎呀。

【讨论】:

你在 XHTML 甚至 HTML 4 中都是对的,但是 HTML 5 handles script tags specially.【参考方案4】:

假设,如果先解析标签,然后再解析 cmets,则 HTML 解析器会为您提供这些结果。

(我并不是说一定是这样,只是一种可能的解释。)

第一种情况

<script><!--
alert('<script></script>');
--></script>

在另一个&lt;script&gt;&lt;/script&gt; 中有一组&lt;script&gt;&lt;/script&gt;。解析器可能会首先忽略标签的名称,只检查这些标签的正确打开和关闭。然后解析 cmets。

<script><!--
--></script>

所以这是有效的。

第二种情况

<script><!--
alert('<script></script>');
</script>

在另一个&lt;script&gt;&lt;/script&gt; 中有一组&lt;script&gt;&lt;/script&gt;。然后解析 cmets。

<script><!--

注释一直延伸到文档的末尾。这不是严格有效的,但浏览器会正确处理它。

第三种情况

<script><!--
alert('</script>');
--></script>

&lt;script&gt;&lt;/script&gt; 集合中有一个结束标记。在将 &lt;/script&gt; 解析为 cmets 之前,它已失效。

第四种情况

<script>
alert('<script></script>');
</script>

在另一个&lt;script&gt;&lt;/script&gt; 中有一组&lt;script&gt;&lt;/script&gt;,并且没有cmets。第一遍是有效的,但它会真正查看标签以查看它们是什么。它可能不接受另一个标签中的一对&lt;script&gt;标签,因此它会使案例无效。

【讨论】:

我认为在第 4 种情况下,脚本标签过早关闭,第一个 &lt;/script&gt; 之后的所有内容都转到同级节点,而第二个“不匹配”&lt;/script&gt; 被忽略。跨度> 第三种情况在 Chrome 中会出现 SyntaxError(可能第一个 &lt;/script&gt; 也关闭了脚本,这里不确定)。

以上是关于浏览器如何准确解析脚本标签?的主要内容,如果未能解决你的问题,请参考以下文章

Hight Performance Javascript——脚本加载和运行

为啥将脚本标签放在正文标签的末尾是好的?

浏览器解析模式

如何确保JavaScript的执行顺序

JavaWebJavaScript

常见(XSS|CSRF)六大Web安全攻防解析