2015-10-20 62 views
1

我有一个表单,用户可以使用TinyMCE进行样式输入描述。因此,我的用户可以插入HTML。我使用strip_tags已经剥离几乎所有的HTML元素,但用户仍然可以输入恶意数据,比如这一个:从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(); 

回答

1

这里有两个选项的DOMDocument :: loadHtml()将解决这个问题。

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

但它们只在libxml> = 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> 
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` */ 
+0

这是几乎等同于我先前发布的代码。我的代码(和你的代码)插入了HTML实体和'html'和'body'标签,这正是我试图阻止的。我需要一个不使用DOMDocument的解决方案,并且不会尝试“修复”HTML(因为HTML并不是整个文档)。 –

+0

为了公平起见,我在现有页面上运行了这段代码,发现没有任何问题 - 当我按照“原样”运行它时,没有找到现有的html标记,就像你说的那样,它已经为自己添加了所有的HTML标记。 – RamRaider

相关问题