从HTML内容中删除脚本标记

时间:2011-08-20 09:18:12

标签: php regex htmlpurifier

我正在使用HTML Purifier(http://htmlpurifier.org/)

我只想删除<script>个标签。 我不想删除内联格式或任何其他内容。

我怎样才能做到这一点?

还有一件事,还有其他方法可以从HTML中删除脚本标记

13 个答案:

答案 0 :(得分:119)

因为这个问题用标记,我将在这种情况下用穷人的解决方案回答:

$html = preg_replace('#<script(.*?)>(.*?)</script>#is', '', $html);

然而,正则表达式不是用于解析HTML / XML,即使你编写完美表达式它最终会破坏,它也不值得,尽管在某些情况下快速修复一些是有用的标记,以及快速修复,忘记安全性。仅对您信任的内容/标记使用正则表达式。

请记住,用户输入的任何内容都应被视为不安全

这里的

更好解决方案是使用专为此设计的DOMDocument。 这是一个片段,演示了如何轻松,干净(与正则表达式相比),(几乎)可靠和(几乎)安全是这样做的:

<?php

$html = <<<HTML
...
HTML;

$dom = new DOMDocument();

$dom->loadHTML($html);

$script = $dom->getElementsByTagName('script');

$remove = [];
foreach($script as $item)
{
  $remove[] = $item;
}

foreach ($remove as $item)
{
  $item->parentNode->removeChild($item); 
}

$html = $dom->saveHTML();

我故意删除了HTML,因为即使这样也可以 bork

答案 1 :(得分:36)

使用PHP DOMDocument解析器。

$doc = new DOMDocument();

// load the HTML string we want to strip
$doc->loadHTML($html);

// get all the script tags
$script_tags = $doc->getElementsByTagName('script');

$length = $script_tags->length;

// for each tag, remove it from the DOM
for ($i = 0; $i < $length; $i++) {
  $script_tags->item($i)->parentNode->removeChild($script_tags->item($i));
}

// get the HTML string back
$no_script_html_string = $doc->saveHTML();

这让我使用以下HTML文档:

<!doctype html>
<html>
    <head>
        <meta charset="utf-8">
        <title>
            hey
        </title>
        <script>
            alert("hello");
        </script>
    </head>
    <body>
        hey
    </body>
</html>

请记住,DOMDocument解析器需要PHP 5或更高版本。

答案 2 :(得分:2)

$html = <<<HTML
...
HTML;
$dom = new DOMDocument();
$dom->loadHTML($html);
$tags_to_remove = array('script','style','iframe','link');
foreach($tags_to_remove as $tag){
    $element = $dom->getElementsByTagName($tag);
    foreach($element  as $item){
        $item->parentNode->removeChild($item);
    }
}
$html = $dom->saveHTML();

答案 3 :(得分:1)

我一直在努力解决这个问题。我发现你真的只需要一个功能。爆炸(&#39;&gt;&#39;,$ html);任何标签的单个共同点是&lt;和&gt;。之后,它通常是引号(&#34;)。一旦找到共同点,您就可以轻松地提取信息。这就是我想出的:

$html = file_get_contents('http://some_page.html');

$h = explode('>', $html);

foreach($h as $k => $v){

    $v = trim($v);//clean it up a bit

    if(preg_match('/^(<script[.*]*)/ius', $v)){//my regex here might be questionable

        $counter = $k;//match opening tag and start counter for backtrace

        }elseif(preg_match('/([.*]*<\/script$)/ius', $v)){//but it gets the job done

            $script_length = $k - $counter;

            $counter = 0;

            for($i = $script_length; $i >= 0; $i--){
                $h[$k-$i] = '';//backtrace and clear everything in between
                }
            }           
        }
for($i = 0; $i <= count($h); $i++){
    if($h[$i] != ''){
    $ht[$i] = $h[$i];//clean out the blanks so when we implode it works right.
        }
    }
$html = implode('>', $ht);//all scripts stripped.


echo $html;

我认为这只适用于脚本标记,因为你永远不会有嵌套的脚本标记。当然,您可以轻松添加更多执行相同检查的代码并收集嵌套标记。

我称之为手风琴编码。爆();爆炸();如果你有一个共同点,那么这是让你的逻辑流动最简单的方法。

答案 4 :(得分:1)

尝试此 完整 灵活 解决方案。它运行良好,并且部分基于 some 先前的答案,但包含其他验证检查,并且从loadHTML(...)函数中删除了其他隐含的 HTML 。它分为两个单独的函数(一个具有先前的依赖关系,因此请勿重新排序/重新排列),因此您可以将其与要同时删除的多个HTML标记一起使用(即,不仅'script'标签)。例如,removeAllInstancesOfTag(...)函数接受标记名的array,或者仅接受一个作为string的标记名。所以,事不宜迟,这里是代码:


/* Remove all instances of a particular HTML tag (e.g. <script>...</script>) from a variable containing raw HTML data. [BEGIN] */

/* Usage Example: $scriptless_html = removeAllInstancesOfTag($html, 'script'); */

if (!function_exists('removeAllInstancesOfTag'))
    {
        function removeAllInstancesOfTag($html, $tag_nm)
            {
                if (!empty($html))
                    {
                        $html = mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'); /* For UTF-8 Compatibility. */
                        $doc = new DOMDocument();
                        $doc->loadHTML($html,LIBXML_HTML_NOIMPLIED|LIBXML_HTML_NODEFDTD|LIBXML_NOWARNING);

                        if (!empty($tag_nm))
                            {
                                if (is_array($tag_nm))
                                    {
                                        $tag_nms = $tag_nm;
                                        unset($tag_nm);

                                        foreach ($tag_nms as $tag_nm)
                                            {
                                                $rmvbl_itms = $doc->getElementsByTagName(strval($tag_nm));
                                                $rmvbl_itms_arr = [];

                                                foreach ($rmvbl_itms as $itm)
                                                    {
                                                        $rmvbl_itms_arr[] = $itm;
                                                    };

                                                foreach ($rmvbl_itms_arr as $itm)
                                                    {
                                                        $itm->parentNode->removeChild($itm);
                                                    };
                                            };
                                    }
                                else if (is_string($tag_nm))
                                    {
                                        $rmvbl_itms = $doc->getElementsByTagName($tag_nm);
                                        $rmvbl_itms_arr = [];

                                        foreach ($rmvbl_itms as $itm)
                                            {
                                                $rmvbl_itms_arr[] = $itm;
                                            };

                                        foreach ($rmvbl_itms_arr as $itm)
                                            {
                                                $itm->parentNode->removeChild($itm); 
                                            };
                                    };
                            };

                        return $doc->saveHTML();
                    }
                else
                    {
                        return '';
                    };
            };
    };

/* Remove all instances of a particular HTML tag (e.g. <script>...</script>) from a variable containing raw HTML data. [END] */

/* Remove all instances of dangerous and pesky <script> tags from a variable containing raw user-input HTML data. [BEGIN] */

/* Prerequisites: 'removeAllInstancesOfTag(...)' */

if (!function_exists('removeAllScriptTags'))
    {
        function removeAllScriptTags($html)
            {
                return removeAllInstancesOfTag($html, 'script');
            };
    };

/* Remove all instances of dangerous and pesky <script> tags from a variable containing raw user-input HTML data. [END] */


这是一个 test 用法示例:


$html = 'This is a JavaScript retention test.<br><br><span id="chk_frst_scrpt">Congratulations! The first \'script\' tag was successfully removed!</span><br><br><span id="chk_secd_scrpt">Congratulations! The second \'script\' tag was successfully removed!</span><script>document.getElementById("chk_frst_scrpt").innerHTML = "Oops! The first \'script\' tag was NOT removed!";</script><script>document.getElementById("chk_secd_scrpt").innerHTML = "Oops! The second \'script\' tag was NOT removed!";</script>';
echo removeAllScriptTags($html);

我希望我的回答对某人有帮助。享受吧!

答案 5 :(得分:0)

如果它可用,我会使用BeautifulSoup。使这种事情变得非常容易。

不要尝试使用regexp。那种方式就是疯狂。

答案 6 :(得分:0)

更短的:

$html = preg_replace("/<script.*?\/script>/s", "", $html);

在进行正则表达式时,事情可能会出错,所以这样做更安全:

$html = preg_replace("/<script.*?\/script>/s", "", $html) ? : $html;

这样当&#34;意外&#34;碰巧,我们得到原始的$ html而不是空字符串。

答案 7 :(得分:0)

  • 这是 ClandestineCoder 和&amp;的合并。 Binh WPO

脚本标记箭头的问题是它们可以有多个变体

  

离。 (&lt; = &lt; = &amp;lt;)&amp; (&gt; = &gt; = &amp;gt;

所以不要创建一个像bazillion变体一样的模式数组, imho是一个更好的解决方案

return preg_replace('/script.*?\/script/ius', '', $text)
       ? preg_replace('/script.*?\/script/ius', '', $text)
       : $text;

这将删除任何看似script.../script的内容,无论箭头代码/变体如何,您都可以在此处https://regex101.com/r/lK6vS8/1进行测试

答案 8 :(得分:0)

修改ctf0答案的示例。这应该只执行preg_replace一次,但也检查错误并阻止正斜杠的char代码。

$str = '<script> var a - 1; <&#47;script>'; 

$pattern = '/(script.*?(?:\/|&#47;|&#x0002F;)script)/ius';
$replace = preg_replace($pattern, '', $str); 
return ($replace !== null)? $replace : $str;  

如果您使用的是PHP 7,则可以使用null coalesce运算符来进一步简化它。

$pattern = '/(script.*?(?:\/|&#47;|&#x0002F;)script)/ius'; 
return (preg_replace($pattern, '', $str) ?? $str); 

答案 9 :(得分:0)

这是Dejan Marjanovic答案的简化版本:

function removeTags($html, $tag) {
    $dom = new DOMDocument();
    $dom->loadHTML($html);
    foreach (iterator_to_array($dom->getElementsByTagName($tag)) as $item) {
        $item->parentNode->removeChild($item);
    }
    return $dom->saveHTML();
}

可用于删除任何类型的标记,包括<script>

$scriptlessHtml = removeTags($html, 'script');

答案 10 :(得分:0)

使用str_replace函数将其替换为空白空间或其他内容
    

$query = '<script>console.log("I should be banned")</script>';

$badChar = array('<script>','</script>');
$query = str_replace($badChar, '', $query);

echo $query; 
//this echoes console.log("I should be banned")

?>

答案 11 :(得分:0)

一种处理字符串的简单方法。

$str = stripStr($str, '<script', '</script>');

function stripStr($str, $ini, $fin)
{
    while(($pos = mb_stripos($str, $ini)) !== false)
    {
        $aux = mb_substr($str, $pos + mb_strlen($ini));
        $str = mb_substr($str, 0, $pos).mb_substr($aux, mb_stripos($aux, $fin) + mb_strlen($fin));
    }

    return $str;
}

答案 12 :(得分:0)

function remove_script_tags($html){
    $dom = new DOMDocument();
    $dom->loadHTML($html);
    $script = $dom->getElementsByTagName('script');

    $remove = [];
    foreach($script as $item){
        $remove[] = $item;
    }

    foreach ($remove as $item){
        $item->parentNode->removeChild($item);
    }

    $html = $dom->saveHTML();
    $html = preg_replace('/<!DOCTYPE.*?<html>.*?<body><p>/ims', '', $html);
    $html = str_replace('</p></body></html>', '', $html);
    return $html;
}

Dejan的回答很好,但是saveHTML()添加了不必要的doctype和body标签,这应该摆脱它。参见https://3v4l.org/82FNP