PHP中的XSS过滤功能
Posted
技术标签:
【中文标题】PHP中的XSS过滤功能【英文标题】:XSS filtering function in PHP 【发布时间】:2010-11-23 03:12:45 【问题描述】:有没有人知道从表单中过滤通用输入的好功能? Zend_Filter_input 似乎需要对输入内容的先验知识,我担心使用 html Purifier 之类的东西会对性能产生很大影响。
类似的东西呢:http://snipplr.com/view/1848/php--sacar-xss/
非常感谢您的任何意见。
【问题讨论】:
HTMLPUrifier 可能会占用一些资源,但您可能没有发布那么多内容? (例如,与所咨询的相比);;如果您在将数据保存到数据库时运行 HTMLPurifier,而不是在从数据库读取数据时运行,则可能没问题... 【参考方案1】:简单的方法?使用strip_tags()
:
$str = strip_tags($input);
您也可以为此使用filter_var()
:
$str = filter_var($input, FILTER_SANITIZE_STRING);
filter_var()
的优点是您可以通过例如剥离或编码低位和高位字符来控制行为。
这是sanitizing filters的列表。
【讨论】:
那么这是最好的方法还是 HTML Purifier 是获得最大安全性的最佳方法。 虽然 cletus 通常很容易被发现,但使用普通的旧strip_tags()
是一个巨大的疏忽和安全问题。详情请阅读以下htmlpurifier.org/comparison#striptags
strip_tags
的问题在于它不验证属性。所以只要你不允许任何标签,它就应该作为一个 XSS 过滤器。
这不能防止像" onclick="alert('Why it is not blocked?')
这样的东西
除非你想剥离每个标签,否则这不是一个安全的 XSS 过滤器。 HTML Purifier(和类似的)库是允许标签和合理安全用户提供的数据的唯一有效方法。【参考方案2】:
黑客使用多种方式进行 XSS 攻击,PHP 的内置函数无法响应各种 XSS 攻击。因此,strip_tags、filter_var、mysql_real_escape_string、htmlentities、htmlspecialchars 等函数并不能 100% 保护我们。你需要一个更好的机制,下面是解决方案:
function xss_clean($data)
// Fix &entity\n;
$data = str_replace(array('&','<','>'), array('&amp;','&lt;','&gt;'), $data);
$data = preg_replace('/(&#*\w+)[\x00-\x20]+;/u', '$1;', $data);
$data = preg_replace('/(&#x*[0-9A-F]+);*/iu', '$1;', $data);
$data = html_entity_decode($data, ENT_COMPAT, 'UTF-8');
// Remove any attribute starting with "on" or xmlns
$data = preg_replace('#(<[^>]+?[\x00-\x20"\'])(?:on|xmlns)[^>]*+>#iu', '$1>', $data);
// Remove javascript: and vbscript: protocols
$data = preg_replace('#([a-z]*)[\x00-\x20]*=[\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', '$1=$2nojavascript...', $data);
$data = preg_replace('#([a-z]*)[\x00-\x20]*=([\'"]*)[\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', '$1=$2novbscript...', $data);
$data = preg_replace('#([a-z]*)[\x00-\x20]*=([\'"]*)[\x00-\x20]*-moz-binding[\x00-\x20]*:#u', '$1=$2nomozbinding...', $data);
// Only works in IE: <span style="width: expression(alert('Ping!'));"></span>
$data = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?expression[\x00-\x20]*\([^>]*+>#i', '$1>', $data);
$data = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?behaviour[\x00-\x20]*\([^>]*+>#i', '$1>', $data);
$data = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:*[^>]*+>#iu', '$1>', $data);
// Remove namespaced elements (we do not need them)
$data = preg_replace('#</*\w+:\w[^>]*+>#i', '', $data);
do
// Remove really unwanted tags
$old_data = $data;
$data = preg_replace('#</*(?:applet|b(?:ase|gsound|link)|embed|frame(?:set)?|i(?:frame|layer)|l(?:ayer|ink)|meta|object|s(?:cript|tyle)|title|xml)[^>]*+>#i', '', $data);
while ($old_data !== $data);
// we are done...
return $data;
【讨论】:
嘿@Sarfraz 你的函数真的安全吗? 你还应该在前面加上 urldecode 这个脚本在 %22%3E%3Cscript%3Ealert('try_xss');%3C/ 上不起作用脚本%3E Christian Stocker 提供了此代码的更新:blog.liip.ch/archive/2008/09/10/… 不适用于" onfocus="alert(1)" autofocus="
【参考方案3】:
最好和最安全的方法是使用 HTML Purifier。按照此链接获取有关将其与 Zend Framework 一起使用的一些提示。
HTML Purifier with Zend Framework
【讨论】:
但是该死的图书馆是臃肿的。 它可能会臃肿,但当你真的需要核选项将其过滤掉时,它是最好的。【参考方案4】:我也有类似的问题。我需要用户使用出色的所见即所得编辑器(Redactorjs!)将 html 内容提交到个人资料页面,我编写了以下函数来清理提交的 html:
<?php function filterxss($str)
//Initialize DOM:
$dom = new DOMDocument();
//Load content and add UTF8 hint:
$dom->loadHTML('<meta http-equiv="content-type" content="text/html; charset=utf-8">'.$str);
//Array holds allowed attributes and validation rules:
$check = array('src'=>'#(http://[^\s]+(?=\.(jpe?g|png|gif)))#i','href'=>'|^http(s)?://[a-z0-9-]+(.[a-z0-9-]+)*(:[0-9]+)?(/.*)?$|i');
//Loop all elements:
foreach($dom->getElementsByTagName('*') as $node)
for($i = $node->attributes->length -1; $i >= 0; $i--)
//Get the attribute:
$attribute = $node->attributes->item($i);
//Check if attribute is allowed:
if( in_array($attribute->name,array_keys($check)))
//Validate by regex:
if(!preg_match($check[$attribute->name],$attribute->value))
//No match? Remove the attribute
$node->removeAttributeNode($attribute);
else
//Not allowed? Remove the attribute:
$node->removeAttributeNode($attribute);
var_dump($dom->saveHTML()); ?>
$check 数组包含所有允许的属性和验证规则。也许这对你们中的一些人有用。我还没有测试,所以欢迎提示
【讨论】:
【参考方案5】:function clean($data)
$data = rawurldecode($data);
return filter_var($data, FILTER_SANITIZE_SPEC_CHARS);
【讨论】:
不起作用。但$data = filter_var($_GET['data'], FILTER_SANITIZE_STRING);
有效。【参考方案6】:
htmlspecialchars()
非常适合过滤以 html 表单显示的用户输入。
【讨论】:
【参考方案7】:根据 www.mcafeesecure.com 对于易受跨站脚本 (XSS) 过滤功能的一般解决方案可以是:
function xss_cleaner($input_str)
$return_str = str_replace( array('<','>',"'",'"',')','('), array('<','>',''','"',')','('), $input_str );
$return_str = str_ireplace( '%3Cscript', '', $return_str );
return $return_str;
【讨论】:
先前接受且高度赞成的答案提供了一个干净而简短的解决方案。您的解决方案对该答案有何补充?查看metaSO question 和Jon Skeet: Coding Blog,了解如何给出正确答案。【参考方案8】:以上所有方法都不允许保留一些标签,如<a>
、<table>
等。有一个终极解决方案http://sourceforge.net/projects/kses/
Drupal 使用它
【讨论】:
【参考方案9】:我正在通过网络收集大部分问题并为所有问题组合步进过滤器。
经过一些测试,它看起来很完美:
/*
* Total XSS preventer class by Full-R
*
*/
final class xCleaner
public static function clean( string $html ): string
return self::cleanXSS(
preg_replace(
[
'/\s?<iframe[^>]*?>.*?<\/iframe>\s?/si',
'/\s?<style[^>]*?>.*?<\/style>\s?/si',
'/\s?<script[^>]*?>.*?<\/script>\s?/si',
'#\son\w*="[^"]+"#',
],
[
'',
'',
''
],
$html
)
);
protected static function hexToSymbols( string $s ): string
return html_entity_decode($s, ENT_XML1, 'UTF-8');
protected static function escape( string $s, string $m = 'attr' ): string
preg_match_all('/data:\w+\/([a-zA-Z]*);base64,(?!_#_#_)([^)\'"]*)/mi', $s, $b64, PREG_OFFSET_CAPTURE);
if( count( array_filter( $b64 ) ) > 0 )
switch( $m )
case 'attr':
$xclean = self::cleanXSS(
urldecode(
base64_decode(
$b64[ 2 ][ 0 ][ 0 ]
)
)
);
break;
case 'tag':
$xclean = self::cleanTagInnerXSS(
urldecode(
base64_decode(
$b64[ 2 ][ 0 ][ 0 ]
)
)
);
break;
return substr_replace(
$s,
'_#_#_'. base64_encode( $xclean ),
$b64[ 2 ][ 0 ][ 1 ],
strlen( $b64[ 2 ][ 0 ][ 0 ] )
);
else
return $s;
protected static function cleanXSS( string $s ): string
// base64 injection prevention
$st = self::escape( $s, 'attr' );
return preg_replace([
// JSON unicode
'/\\\\u??([a-f0-9]4,?)?/mi', // [1] unicode JSON clean
// Data b64 safe
'/\*\w*\*/mi', // [2] unicode simple clean
// Malware payloads
'/:?e[\s]*x[\s]*p[\s]*r[\s]*e[\s]*s[\s]*s[\s]*i[\s]*o[\s]*n[\s]*(:|;|,)?\w*/mi', // [3] (:expression) evalution
'/l[\s]*i[\s]*v[\s]*e[\s]*s[\s]*c[\s]*r[\s]*i[\s]*p[\s]*t[\s]*(:|;|,)?\w*/mi', // [4] (livescript:) evalution
'/j[\s]*s[\s]*c[\s]*r[\s]*i[\s]*p[\s]*t[\s]*(:|;|,)?\w*/mi', // [5] (jscript:) evalution
'/j[\s]*a[\s]*v[\s]*a[\s]*s[\s]*c[\s]*r[\s]*i[\s]*p[\s]*t[\s]*(:|;|,)?\w*/mi', // [6] (javascript:) evalution
'/b[\s]*e[\s]*h[\s]*a[\s]*v[\s]*i[\s]*o[\s]*r[\s]*(:|;|,)?\w*/mi', // [7] (behavior:) evalution
'/v[\s]*b[\s]*s[\s]*c[\s]*r[\s]*i[\s]*p[\s]*t[\s]*(:|;|,)?\w*/mi', // [8] (vsbscript:) evalution
'/v[\s]*b[\s]*s[\s]*(:|;|,)?\w*/mi', // [9] (vbs:) evalution
'/e[\s]*c[\s]*m[\s]*a[\s]*s[\s]*c[\s]*r[\s]*i[\s]*p[\s]*t*(:|;|,)?\w*/mi', // [10] (ecmascript:) possible ES evalution
'/b[\s]*i[\s]*n[\s]*d[\s]*i[\s]*n[\s]*g*(:|;|,)?\w*/mi', // [11] (-binding) payload
'/\+\/v(8|9|\+|\/)?/mi', // [12] (UTF-7 mutation)
// Some entities
'/&\w*\w*/mi', // [13] html entites clenup
'/&#\d+;?/m', // [14] html entites clenup
// Script tag encoding mutation issue
'/\¼\/?\w*\¾\w*/mi', // [21] mutation KOI-8
'/\+ADw-\/?\w*\+AD4-\w*/mi', // [22] mutation old encodings
'/\/*?%00*?\//m',
// base64 escaped
'/_#_#_/mi', // [23] base64 escaped marker cleanup
],
// Replacements steps :: 23
['&#x$1;', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', ''],
str_ireplace(
['\u0', ':', '&tab;', '&newline;'],
['\0', ':', '', ''],
// U-HEX prepare step
self::hexToSymbols( $st ))
);
您还可以添加整洁的标记更正以使 HTML 有效。
【讨论】:
【参考方案10】:我找到了解决德语变音符号帖子问题的方法。为了完全清理(杀死)帖子,我对传入的数据进行编码:
*$data = utf8_encode($data);
... function ...*
最后我解码输出以获得正确的符号:
*$data = utf8_decode($data);*
现在帖子通过过滤功能,我得到了正确的结果...
【讨论】:
【参考方案11】:尝试使用 Clean XSS
xss_clean($data): "><script>alert(String.fromCharCode(74,111,104,116,111,32,82,111,98,98,105,101))</script>
【讨论】:
以上是关于PHP中的XSS过滤功能的主要内容,如果未能解决你的问题,请参考以下文章
filter_var 函数()使用javascript伪协议绕过执行xss