HTML.innerHTML与Jquery.html() - Javascript执行

4

参考:html() vs innerHTML jquery/javascript & XSS attacks

由此可以推断出,JQuery会从DOM中提取<script>标签并在DOM中单独执行,因此它不会出现在DOM中。

考虑以下HTML代码:

a = <iframe><iframe //><script>alert(1)</script>

b = <iframe><iframe> //<script>alert(1)</script>

针对a的代码,body.innerHTML = a;不会执行脚本,但是$("body").html(a);可以执行。

为什么? JQuery的.html()执行//后的内容,但.innerHTML=不执行吗?

如果是这样,为什么b无论是在.innerHTML=还是.html()中都不会被执行呢?

更新:要演示,请打开控制台并执行以下操作:

  1. document.body.innerHTML = "<iframe><iframe //><script>alert(1)</script>"
  2. $("body").html("<iframe><iframe //><script>alert(1)</script>");

第一个不会执行警报,但第二个会。用b替换HTML值。两个都不会被执行。

更新2:从我所确定的情况来看,在Jquery的body()中可以执行HTML代码,但在.innerHTML=中不行?


4
“//” 不是 HTML 注释……它们的外观类似于“<!-- -->”。 - charlietfl
1
@BharatPatidar 我知道这是错误的。但是,对于这个格式不正确的HTML代码,为什么.innerHTML()和Jquery的.html()之间会有差异呢? - verstappen_doodle
3
innerHTML和jQuery的.html()有什么区别? - prasanth
是的,我不会直接回答你的问题。我只会在评论中提到与你问题相关的话题。 - prasanth
在情况b)中,<iframe><iframe> //<script>alert(1)</script> ,//位于任何标签之外,请尝试将它们放入像<div><iframe><iframe> //<script>alert(1)</script> </div>这样的标签中,然后它也会执行。 - Netham
显示剩余12条评论
3个回答

2
如果深入研究jQuery源代码,我们可以找到html方法。
在这个方法中存在下一个
this.empty().append( value );

如果现在前往 append,我们可以找到下一个。
append: function() {
    return domManip( this, arguments, function( elem ) {
        if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
            var target = manipulationTarget( this, elem );
            target.appendChild( elem );
        }
    } );
}

所以,现在找到domManip。在这个函数中,从HTML字符串构建出片段,如果片段中有脚本标记则执行下一个代码
DOMEval( node.textContent.replace( rcleanScript, "" ), doc );

在哪里 DOMEval

function DOMEval( code, doc ) {
    doc = doc || document;

    var script = doc.createElement( "script" );

    script.text = code;
    doc.head.appendChild( script ).parentNode.removeChild( script );
}

所以,至少我们找到了执行脚本的地方。
所以,为什么有些情况下 html 可以运行脚本,而有些情况下却不行?
这取决于输入字符串以及返回值 buildFragment 函数
buildFragment 中,我们可以找到 下一行
tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ];

其中,elem 是输入字符串,jQuery.htmlPrefilter 是下一个函数。
htmlPrefilter: function( html ) {
    return html.replace( rxhtmlTag, "<$1></$2>" );
}

所以,输入字符串只是被一些正则表达式rxhtmlTag替换了。
rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([a-z][^\/\0>\x20\t\r\n\f]*)[^>]*)\/>/gi,

所以,只需尝试使用以下方法检查字符串:

console.log(jQuery.htmlPrefilter("<iframe><iframe //><script>alert(1)</" + "script>"));
console.log(jQuery.htmlPrefilter("<iframe><iframe> // <script>alert(1)</" + "script>"));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.1/jquery.min.js"></script>

所以,在第一种情况下,结果是:

<iframe><iframe /></iframe><script>alert(1)</script>

在第一个例子中,将其作为innerHTML插入到名为“tmp”的div中,在该div内创建两个元素:iframe和script。因此,在此之后,可以找到并执行此脚本。
在第二种情况下:
<iframe><iframe> // <script>alert(1)</script>

字符串未被更改,将其作为innerHTML插入到“tmp” div中,在div内仅创建一个带有编码内容的iframe元素。这就是为什么在这种情况下脚本不会执行的原因。

尽管很有趣,但文档中也非常明确地说明了:按设计,任何接受HTML字符串的jQuery构造函数或方法——jQuery()、.append()、.after()等——都可能执行代码。这可能是通过注入脚本标记或使用执行代码的HTML属性(例如<img onload="">)来实现的。 - Mackan
@elfan,我在这段代码中遇到了下一个错误_Uncaught SyntaxError: Invalid or unexpected token_。 - Grundy
@Grundy,我也是。我不知道为什么,但当我重新输入时,它就没有语法错误了。 - elfan
@elfan,我在这一行找到了答案:tmp.innerHTML = wrap[ 1 ] + elem.replace( rxhtmlTag, "<$1></$2>" ) + wrap[ 2 ]; 在某些情况下,它会将所有的包装标签替换为 iframe 标签,并对 iframe 中的脚本和其他标签进行编码。而在另一种情况下,在 iframe 中只有 <iframe //>,而脚本则按原样附加,以便于下一步查找和执行。 - Grundy
@elfan,我更新了答案。你的回答很正确,但没有考虑预处理输入的HTML字符串。 - Grundy
显示剩余3条评论

2
我认为这里发生了什么。
<iframe>//<script>alert(1)</script>

由于//<script>alert(1)</script>被看作是未关闭的<iframe>标签的内容,因此它不会被执行。我们知道,iframe的内容将被忽略(只有不支持iframe的浏览器才会处理它)。

另一方面,

<iframe//><script>alert(1)</script>

此代码被执行是因为 <iframe//> 被视为 <iframe />,就像 <br /> 一样(至少在 Chrome、FF、Edge 和 IE 中)。现在,iframe 元素已经完成,下一个元素 (<script>alert(1)</alert>) 将被处理。


在看到@Grundy的答案并阅读了我的评论后,我对自己的答案更加有信心,因为我已经了解了jQuery代码的内部工作原理。 - elfan

-1

首先让我们看看除了你提到的之外,会执行什么

$("body").html("<iframe//> <script>alert(1)</script>");
$("body").html("<iframe//> // <script>alert(1)</script>");
$("body").html("<iframe><iframe//> <script>alert(1)</script>");
 $("body").html("<iframe><iframe><iframe><iframe//> <script>alert(1)</script>");

那么什么没有被执行?

$("body").html("<iframe> <script>alert(1)</script>");
$("body").html("<iframe><iframe><iframe> <script>alert(1)</script>");

在从有效代码中的第二个示例中,您可以看到造成问题的不是 //。 因此,可以清楚地观察到,在留下iframe标记打开的情况下,脚本不会执行。
正如我之前在评论中提到的(我忘了在这里包含它),如果关闭iframe标记,则脚本将按照当前页面的参考执行。
更新:
如果您运行此
$("body").html("<iframe> <script>alert(1)</script> ");

并检查 iframe 的 innerHTML,它会显示

&lt;div&gt;Hi&lt;/div&gt; '&lt;script&gt;alert(1)&lt;/script&gt;'

这解释了为什么脚本没有执行。


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