为什么在使用JavaScript生成HTML时要使用\x3C而不是<?

34

我经常看到以下 HTML 代码用于从内容交付网络加载jQuery,但如果CDN不可用,则会回退到本地副本(例如在Modernizr文档中):

<script src="//ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.js"></script>
<script>window.jQuery || document.write('<script src="js/libs/jquery-1.6.1.min.js">\x3C/script>')</script>
我的问题是,为什么在document.write()语句中的最后一个<字符被替换为转义序列\x3C<是JavaScript中的安全字符,甚至在同一个字符串中早些时候就已经使用过了,那么为什么要在这里对其进行转义呢?这样做只是为了防止糟糕的浏览器实现认为字符串中的</script>是真正的脚本结束标记吗?如果是,那么真的有哪些浏览器会失败吗?
作为跟进问题,我还见过几次使用unescape()的变体(如此答案中所示)。是否有原因使得该版本似乎总是替换所有<>字符?

这个问题在脚本文件或者 eval 块中是否成立? - Fordi
2个回答

66
当浏览器看到</script>时,它会认为这是脚本块的结尾(因为HTML解析器不知道JavaScript,无法区分仅出现在字符串中的内容和实际意义上的脚本元素的结束)。因此,在HTML页面内部的JavaScript中,字面出现的</script>会导致错误(在最好的情况下),并且可能成为巨大的安全漏洞(在最坏的情况下)。
这就是为什么你必须防止这个字符序列出现。其他常见的解决办法是"<"+"/script>""<\/script>"(它们都归结为同一件事)。
虽然有些人认为这是一个“错误”,但根据specification,用户代理的HTML部分与脚本引擎完全分离,因此必须以这种方式发生。您可以将各种内容放入<script>标记中,不仅限于JavaScript。 W3C提到了VBScript和TCL作为示例。另一个例子是jQuery template plugin,它也使用这些标记。
但是,在JavaScript中,即使在字符串中识别此类内容并因此不将其视为结束标记,下一个歧义也会出现,即考虑注释:
<script type="text/javascript">foo(42); // call the function </script>

- 在这种情况下,浏览器应该怎么做?

最后,那些甚至不了解JavaScript的浏览器会怎样呢?它们只会忽略<script></script>之间的部分,但是如果你根据浏览器对JavaScript的了解程度给字符序列</script>赋予不同的语义,那么在HTML解析阶段中就会出现两个不同的结果。

最后,关于你提到的替换所有角括号的问题:我认为至少在99%的情况下,这是为了混淆代码,即隐藏(从反病毒软件、审查代理(就像你的例子中嵌套的圆括号一样)等)你的JavaScript正在进行一些类似HTML的操作。我想不出除了</script>以外还有什么好的技术原因来隐藏任何东西,至少对于相当现代的浏览器而言(我的意思是几乎比Mosaic更新的任何浏览器)。


2
大多数情况下,我认为Chrome会将其解释为闭合标签。(昨天检查过) - J. K.
用CDATA部分包装脚本会发生这种情况吗? - Marcin
1
@Mark - 所有浏览器都会“失败”,因为</script>应该被视为结束标记。 - Quentin
1
@Marcin — 如果文档以application/xhtml+xml的形式提供并作为XML解析,或者如果使用真正的SGML解析器解析文档,则不会出现这种情况...但是如果使用标签堆或HTML 5解析器,则会出现这种情况。 - Quentin
@Quentin,太棒了,谢谢您的澄清。HTML5改变了什么行为? - Marcin
显示剩余3条评论

2
一些解析器会将<版本视为闭合标签,并将其解释为...
<script>
  window.jQuery || document.write('<script src="js/libs/jquery-1.6.1.min.js">
</script>

\x3C 是十六进制表示的 <。在脚本中两者可以互换使用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接