是我反XSS方法"确定"对允许用户HTML在PHP?
题
我正在寻找一个很好的方法,使用户提出的数据,在这种情况下允许HTML,并让它作为安全和快,我可以。
我知道每个人在这个网站上似乎认为 http://htmlpurifier.org 是这里的答案。我不同意部分。 htmlpurifier 有最好的开放源代码的出有用于筛选的用户提交HTML但有的方案是非常庞大和不利于业绩上的一个高流量网站。我甚至可能使用还有方案有一天但是现在我的目标是找到一个更轻便的方法。
我已经使用的2职能下为大约2个半年,现在没有任何问题,但是我认为现在是时候采取一些输入从亲就在这里如果他们会帮助我。
第一个函数 FilterHTML($string) 它跑前用户的数据保存到一个mysql数据库。第二项功能是所谓的 format_db_value($text,$nl2br=false) 和我使用它在一个页面,我计划,以显示用户的提交数据。
以下2个职能是一堆的XSS码我发现在 http://ha.ckers.org/xss.html 然后我跑了他们对这些2功能看到的情感,我的代码,我有点高兴的结果,他们没有方块出来的每一个码我尝试过的但我知道它仍然不是100%安全的明显的。
你们能不能请你看过它,给我任何建议,我的代码本身或甚至在整个html过滤的概念。
我想做一个白名单办法,但总有一天 htmlpurifier 是唯一的解决办法,我发现值得采用,并正如我提到的这不是轻量级的,因为我会喜欢的。
function FilterHTML($string) {
if (get_magic_quotes_gpc()) {
$string = stripslashes($string);
}
$string = html_entity_decode($string, ENT_QUOTES, "ISO-8859-1");
// convert decimal
$string = preg_replace('/&#(\d+)/me', "chr(\\1)", $string); // decimal notation
// convert hex
$string = preg_replace('/&#x([a-f0-9]+)/mei', "chr(0x\\1)", $string); // hex notation
//$string = html_entity_decode($string, ENT_COMPAT, "UTF-8");
$string = preg_replace('#(&\#*\w+)[\x00-\x20]+;#U', "$1;", $string);
$string = preg_replace('#(<[^>]+[\s\r\n\"\'])(on|xmlns)[^>]*>#iU', "$1>", $string);
//$string = preg_replace('#(&\#x*)([0-9A-F]+);*#iu', "$1$2;", $string); //bad line
$string = preg_replace('#/*\*()[^>]*\*/#i', "", $string); // REMOVE /**/
$string = preg_replace('#([a-z]*)[\x00-\x20]*([\`\'\"]*)[\\x00-\x20]*j[\x00-\x20]*a[\x00-\x20]*v[\x00-\x20]*a[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iU', '...', $string); //JAVASCRIPT
$string = preg_replace('#([a-z]*)([\'\"]*)[\x00-\x20]*v[\x00-\x20]*b[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iU', '...', $string); //VBSCRIPT
$string = preg_replace('#([a-z]*)[\x00-\x20]*([\\\]*)[\\x00-\x20]*@([\\\]*)[\x00-\x20]*i([\\\]*)[\x00-\x20]*m([\\\]*)[\x00-\x20]*p([\\\]*)[\x00-\x20]*o([\\\]*)[\x00-\x20]*r([\\\]*)[\x00-\x20]*t#iU', '...', $string); //@IMPORT
$string = preg_replace('#([a-z]*)[\x00-\x20]*e[\x00-\x20]*x[\x00-\x20]*p[\x00-\x20]*r[\x00-\x20]*e[\x00-\x20]*s[\x00-\x20]*s[\x00-\x20]*i[\x00-\x20]*o[\x00-\x20]*n#iU', '...', $string); //EXPRESSION
$string = preg_replace('#</*\w+:\w[^>]*>#i', "", $string);
$string = preg_replace('#</?t(able|r|d)(\s[^>]*)?>#i', '', $string); // strip out tables
$string = preg_replace('/(potspace|pot space|rateuser|marquee)/i', '...', $string); // filter some words
//$string = str_replace('left:0px; top: 0px;','',$string);
do {
$oldstring = $string;
//bgsound|
$string = preg_replace('#</*(applet|meta|xml|blink|link|script|iframe|frame|frameset|ilayer|layer|title|base|body|xml|AllowScriptAccess|big)[^>]*>#i', "...", $string);
} while ($oldstring != $string);
return addslashes($string);
}
下面的功能是使用当显示出用户提交码的网页上
function format_db_value($text, $nl2br = false) {
if (is_array($text)) {
$tmp_array = array();
foreach ($text as $key => $value) {
$tmp_array[$key] = format_db_value($value);
}
return $tmp_array;
} else {
$text = htmlspecialchars(stripslashes($text));
if ($nl2br) {
return nl2br($text);
} else {
return $text;
}
}
}
代码下面是从 ha.ckers.org 他们似乎都失败在我的功能上
我没有尝试的每个人在该网站上,虽然有更多的事情,这仅仅是其中的一些。
原始代码顶线的每一个集和代码运行之后通过我的功能上线以下。
<IMG SRC="javascript:alert(\'XSS\');"><b>hello</b> hiii
<IMG SRC=...alert('XSS');"><b>hello</b> hiii
<IMG SRC=JaVaScRiPt:alert('XSS')>
<IMG SRC=...alert('XSS')>
<IMG SRC=javascript:alert(String.fromCharCode(88,83,83))>
<IMG SRC=...alert(String.fromCharCode(88,83,83))>
<IMG SRC=javascript:alert('XSS')>
<IMG SRC=...alert('XSS')>
<IMG SRC=javascript:alert('XSS')>
<IMG SRC=F MLEJNALN !>
<IMG SRC=javascript:alert('XSS')>
<IMG SRC=...alert('XSS')>
<IMG SRC="jav
ascript:alert('XSS');">
<IMG SRC=...alert('XSS');">
perl -e 'print "<IMG SRC=javascript:alert("XSS")>";' > out
perl -e 'print "<IMG SRC=java\0script:alert(\"XSS\")>";' > out
<BODY onload!#$%&()*~+-_.,:;?@[/|\]^`=alert("XSS")>
...
<iframe src=http://ha.ckers.org/scriptlet.html <
...
<LAYER SRC="http://ha.ckers.org/scriptlet.html"></LAYER>
......
<META HTTP-EQUIV="Link" Content="<http://ha.ckers.org/xss.css>; REL=stylesheet">
...; REL=stylesheet">
<IMG STYLE="xss:...(alert('XSS'))">
<IMG STYLE="xss:expr/*XSS*/ession(alert('XSS'))">
<XSS STYLE="xss:...(alert('XSS'))">
<XSS STYLE="xss:expression(alert('XSS'))">
<EMBED SRC="data:image/svg+xml;base64,PHN2ZyB4bWxuczpzdmc9Imh0dH A6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcv MjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hs aW5rIiB2ZXJzaW9uPSIxLjAiIHg9IjAiIHk9IjAiIHdpZHRoPSIxOTQiIGhlaWdodD0iMjAw IiBpZD0ieHNzIj48c2NyaXB0IHR5cGU9InRleHQvZWNtYXNjcmlwdCI+YWxlcnQoIlh TUyIpOzwvc2NyaXB0Pjwvc3ZnPg==" type="image/svg+xml" AllowScriptAccess="always"></EMBED>
<EMBED SRC="data:image/svg+xml;base64,PHN2ZyB4bWxuczpzdmc9Imh0dH A6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcv MjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hs aW5rIiB2ZXJzaW9uPSIxLjAiIHg9IjAiIHk9IjAiIHdpZHRoPSIxOTQiIGhlaWdodD0iMjAw IiBpZD0ieHNzIj48c2NyaXB0IHR5cGU9InRleHQvZWNtYXNjcmlwdCI+YWxlcnQoIlh TUyIpOzwvc2NyaXB0Pjwvc3ZnPg==" type="image/svg+xml" AllowScriptAccess="always"></EMBED>
<IMG
SRC
=
"
j
a
v
a
s
c
r
i
p
t
:
a
l
e
r
t
(
'
X
S
S
'
)
"
>
<IMG
SRC
=...
a
l
e
r
t
(
'
X
S
S
'
)
"
>
解决方案
这里是四个备选方案:
其他提示
只有这样,才能确保是白名单的标签和属性,他们可以使用和写入严格的正则表达式来验证属性的允许值。如果你想允许的属性,如“风格”,那么你有额外的复杂性。
只有黑名单可能使一些人更难攻击,但它不会对你有没有听说过的又使用技术的人做任何困难。
我会尝试使用正则表达式来在它添加缺少的结束标记进入哪些用户和更换<br>
<br />
等,然后使用SimpleXML解析它,然后修改并删除任何标记,不在白名单,即任何属性不在白名单中的给定的标签,并有不符合精确的正则表达式该属性的值的任何属性。毕竟我会使用asXML()来获取背课文。我开始用最少的一组标签和属性,并根据需要被特别小心的任何可能包含URL添加新的。
恕我直言htmlawed是最好的 - 用于标记和属性精干,快速,完整的HTML覆盖,最灵活的...黑名单或白名单。安全? 失利的所有ha.ckers XSS代码
如何使用PHP的本地HTML解析器?
我很好奇,所以我已经写了一段代码,用于测试(要求PHP 5.3.6 +):
$badHtml = file_get_contents('badHtml.txt');
$html = sprintf('<div id="input">%s</div>', $badHtml);
// tidy is no required, but may fix invalid markup
$tidy = new \tidy();
$tidy->parseString($html, array(), 'utf8');
$tidy->cleanRepair();
$dom = new \DomDocument('1.0', 'UTF-8');
libxml_use_internal_errors(true);
$dom->loadHtml($tidy);
$input = $dom->getElementById('input');
// tag as key, attributes as values
$allowed = array(
'table' => array('border'),
'tbody' => array(),
'tr' => array(),
'td' => array(),
'th' => array(),
'img' => array('src', 'alt'),
'p' => array(),
'ul' => array(),
'ol' => array(),
'li' => array(),
'a' => array('href', 'title'),
'strong' => array(),
'em' => array(),
'sub' => array(),
'sup' => array(),
);
$walk = function(\DomNode $node) use($allowed, &$walk){
// only check tags
if($node->nodeType !== XML_ELEMENT_NODE)
return;
if(!isset($allowed[$node->nodeName]))
return $node->parentNode->removeChild($node);
foreach($node->attributes as $key => $attr){
if(!in_array($key, $allowed[$node->nodeName], true))
$node->removeAttribute($key);
// expect URLs here
if(!in_array($key, array('href', 'src'), true))
continue;
if(!filter_var($attr->value, FILTER_VALIDATE_URL))
return $node->parentNode->removeChild($node);
}
array_map($walk, iterator_to_array($node->childNodes));
};
// convert DOMNodeList to array because this way the bad stuff
// can be removed within the loop
array_map($walk, iterator_to_array($input->childNodes));
// export HTML
$sanitized = $dom->saveHtml($input);
的输出,而无需运行整洁:
似乎确定。还是因为它去掉太多了吗? :) 应方式比HTMLPurifier更快,理论上更安全,因为它不太宽容,并可能快于正则表达式了。