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('&','<','>'), $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('&lt;','&gt;','&apos;','&#x22;','&#x29;','&#x28;'), $input_str );
    $return_str = str_ireplace( '%3Cscript', '', $return_str );
    return $return_str;

【讨论】:

先前接受且高度赞成的答案提供了一个干净而简短的解决方案。您的解决方案对该答案有何补充?查看metaSO question 和Jon Skeet: Coding Blog,了解如何给出正确答案。【参考方案8】:

以上所有方法都不允许保留一些标签,如&lt;a&gt;&lt;table&gt; 等。有一个终极解决方案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', '&colon;', '&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过滤功能的主要内容,如果未能解决你的问题,请参考以下文章

xss:利用编码特性绕过过滤

filter_var 函数()使用javascript伪协议绕过执行xss

上传Word文件形成存储型XSS路径

php检查漏洞防护补丁-防护XSS,SQL,文件包含等多种高危漏洞

PHP 通用清洁功能 - 防止SQL,XSS和JS注入

来自阿里云的PHP实现的防止跨站和xss攻击代码,测试有用