DOMDocument从HTML源中删除脚本标记

Ran*_*der 12 php xss script-tag html-parsing domdocument

在这里使用@Alex的方法使用内置的DOMDocument从HTML文档中删除脚本标记.问题是,如果我有一个带有Javascript内容的脚本标记,然后是另一个链接到外部Javascript源文件的脚本标记,则不会从HTML中删除所有脚本标记.

$result = '
<!doctype html>
<html>
    <head>
        <meta charset="utf-8">
        <title>
            hey
        </title>
        <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
        <script>
            alert("hello");
        </script>
    </head>
    <body>hey</body>
</html>
';

$dom = new DOMDocument();
if($dom->loadHTML($result))
{
    $script_tags = $dom->getElementsByTagName('script');

    $length = $script_tags->length;

    for ($i = 0; $i < $length; $i++) {
        if(is_object($script_tags->item($i)->parentNode)) {
            $script_tags->item($i)->parentNode->removeChild($script_tags->item($i));
        }
    }

    echo $dom->saveHTML();
}
Run Code Online (Sandbox Code Playgroud)

以上代码输出:

<html>
    <head>
        <meta charset="utf-8">
        <title>hey</title>
        <script>
        alert("hello");
        </script>
    </head>
    <body>
        hey
    </body>
</html>
Run Code Online (Sandbox Code Playgroud)

从输出中可以看出,只删除了外部脚本标记.有什么办法可以确保删除所有脚本标记吗?

Séb*_*uld 23

你的错误实际上是微不足道的.一个DOMNode对象(及其所有后代- ,DOMElement!DOMNodeList和其他一些),当它的父元素的变化,最明显的是当其子女人数变化自动更新.这是在PHP文档的几行中写的,但大多数都是在地毯下.

如果循环使用($k instanceof DOMNode)->length,并随后从节点中删除元素,您会注意到该length属性实际上已更改!我不得不写自己的库来抵消这个和其他一些怪癖.

解决方案:

if($dom->loadHTML($result))
{
    while (($r = $dom->getElementsByTagName("script")) && $r->length) {
            $r->item(0)->parentNode->removeChild($r->item(0));
    }
echo $dom->saveHTML();
Run Code Online (Sandbox Code Playgroud)

我实际上并没有循环 - 只需一次弹出第一个元素.结果:http://sebrenauld.co.uk/domremovescript.php

  • 另一种解决方案是以相反的顺序循环遍历元素. (6认同)
  • 请注意,这会在使用 loadHTML() 时破坏 DOMDocument 解析,因为 Javascript 字符串中的 HTML 标记:```&lt;div&gt; &lt;script&gt; var str = '&lt;/div&gt;this does not get removed'; &lt;/script&gt; &lt;/div&gt;``` (2认同)

tri*_*cot 8

为了避免您获得实时节点列表的惊喜- 随着删除节点而缩短 - 您可以使用以下方法将副本复制到数组中iterator_to_array:

foreach(iterator_to_array($dom->getElementsByTagName($tag)) as $node) {
    $node->parentNode->removeChild($node);
};  
Run Code Online (Sandbox Code Playgroud)

  • +1 比接受的答案简单得多 - 当您遍历文档进行更改时,似乎必须在节点列表上使用 `iterator_to_array()`。 (3认同)