从样式除外的HTML字符串中剥离所有标记属性

时间:2015-10-20 07:46:57

标签: php html dom

我有一个表单,用户可以使用TinyMCE输入描述,以进行样式设置。因此,我的用户可以插入HTML。我已经使用strip_tags剥离了几乎所有HTML元素,但用户仍然可以输入恶意值,例如:

<strong onclick="window.location='http://example.com'">Evil</strong>

我希望通过剥离所有标记的所有属性来阻止用户执行此操作,但style属性除外。

我只能找到剥离所有属性的解决方案,或者只删除指定的属性。我只想保留style属性。

我尝试过DOMDocument,但它似乎自己添加了DOCTYPEhtml标记,并将其作为整个HTML文档输出。此外,它有时似乎随机添加HTML实体,如颠倒的问号。

这是我的DOMDocument实现:

//Example "evil" input
$description = "<p><strong onclick=\"alert('evil');\">Evil</strong></p>";

//Strip all tags from description except these
$description = strip_tags($description, '<p><br><a><b><i><u><strong><em><span><sup><sub>');

//Strip attributes from tags (to prevent inline Javascript)
$dom = new DOMDocument();
$dom->loadHTML($description);
foreach($dom->getElementsByTagName('*') as $element)
{
    //Attributes cannot be removed directly because DOMNamedNodeMap implements Traversable incorrectly
    //Atributes are first saved to an array and then looped over later
    $attributes_to_remove = array();
    foreach($element->attributes as $name => $value)
    {
        if($name != 'style')
        {
            $attributes_to_remove[] = $name;
        }
    }

    //Loop over saved attributes and remove them
    foreach($attributes_to_remove as $attribute)
    {
        $element->removeAttribute($attribute);
    }
}
echo $dom->saveHTML();

2 个答案:

答案 0 :(得分:1)

以下是DOMDocument :: loadHtml()的两个选项,可以解决问题。

$dom->loadHTML($description,  LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);

但它们仅在libxml&gt; = 2.7.8中可用。如果您有旧版本,可以尝试不同的方法:

如果您知道您希望使用该片段,则可以使用该片段并仅保存body元素的子元素。

$description = <<<'HTML'
<strong onclick="alert('evil');" style="text-align:center;">Evil</strong>
HTML;

$dom = new DOMDocument();
$dom->loadHTML($description);
foreach($dom->getElementsByTagName('*') as $element) {
    $attributes_to_remove = iterator_to_array($element->attributes);
    unset($attributes_to_remove['style']);
    foreach($attributes_to_remove as $attribute => $value) {
        $element->removeAttribute($attribute);
    }
}
foreach ($dom->getElementsByTagName('body')->item(0)->childNodes as $node) {
  echo $dom->saveHTML($node);
}

输出:

<strong style="text-align:center;">Evil</strong>

答案 1 :(得分:0)

我不知道这或多或少是你的意思......

$description = "<p><strong onclick=\"alert('evil');\">Evil</strong></p>";
$description = strip_tags( $description, '<p><br><a><b><i><u><strong><em><span><sup><sub>' );

$dom=new DOMDocument;
$dom->loadHTML( $description );
$tags=$dom->getElementsByTagName('*');

foreach( $tags as $tag ){
    if( $tag->hasAttributes() ){
        $attributes=$tag->attributes;
        foreach( $attributes as $name => $attrib ) $tag->removeAttribute( $name );
    }
}
echo $dom->saveHTML();
/* Will echo out `Evil` in bold but without the `onclick` */