XSS注入我也不怕不怕啦——PHP从框架层面屏蔽XSS的思考和实践

本文由腾讯WeTest团队提供,更多资讯可直接戳链接查看:http://wetest.qq.com/lab/

微信号:TencentWeTest

对于新接触web开发的同学来说,XSS注入是一件非常头疼的事情。就算是web开发多年的老手,也不敢保证自己写的代码完全没有XSS注入的风险。

因为现在比较主流的XSS防治手段主要有两种,一种是在用户输入是将异常关键词过滤,另一种则是在页面渲染时将html内容实体化转义。

然而第一种方法一定程度上对业务数据要求相对较高,存在屏蔽数据和业务数据有冲突的情况,例如“程序类帮助文档的编辑保存”,“外站帖子爬虫”等等。都不能无差别将异常关键词过滤掉,必须保持原输入内容的完整性。

而另一种html内容实体化的方式,又非常的依赖开发的编程习惯。一个不小心漏写了就是一个安全工单,做web的前端同事应该深有体会。于是,我开始研究能不能不再依赖开发习惯,从框架层面上完全屏蔽XSS。

这里先介绍一下我的PHP web Server框架,是我自己从从事web开发开始就一直在维护更新的框架,链接在此,有兴趣的同学,可以看下。或者提出更多改进的建议。

首先来看下普通的PHP是怎么转义html实体的:

htmlspecialchars($content, ENT_QUOTES | ENT_SUBSTITUTE)

ENT_QUOTES 意思是需要转义双引号(")和 单引号 (‘)

ENT_SUBSTITUTE 意思是 把无效的编码替代成一个指定的带有 Unicode 替代字符

首先很容易想到的是把php模版中的字符串全部替换掉。

而熟悉smarty的同学应该知道,其实smarty的模版渲染也是用了转义字符串的方式。那我们渲染页面的代码可以这么写。

/**
     * 获得模板渲染后的内容
     * @return string
     */
    public function getContent()
    {
        //防XSS注入
        foreach ($this->params as &$param) {
            $param = is_string($param) ? htmlspecialchars($param, ENT_QUOTES | ENT_SUBSTITUTE) : $param;
        }
        unset($param);

        extract($this->params);
        ob_start();
        //include template
        $file = sprintf(‘%s/template/%s.tpl.php‘, TXApp::$app_root, $this->view);
        include $file;
        $content = ob_get_clean();
        return $content;
    }

这样的话,传入的字符串类型的变量都会被替换掉了。但是问题也很明显。那就是如果是数组或者object对象,里面的内容就无法进行转义了。而这同样也是smarty的一个弊端,smarty是在assign方法里进行的实体化转义,如果是数组或者object就无视了。当然我们还需要更进一步的进行转义处理。

有同学看到这里肯定会有个想法,如果是数组的话,递归进行转义处理不就可以了吗。

事实上我一开始的确是这么做的,但是弊端也很明显。递归的层数越多,性能损耗就越大。而且并非所有进行转义的内容我们都会用到,这样就会造成性能的浪费。最优化的处理方式就是当需要用到的时候再做转义处理,没用到的时候该咋样还是咋样。

于是我开始着手自己写一个类,在我的框架里我命名为TXArray 继承了ArrayObject,也就是让其具备了array的部分性质。接下来开始进行array 方法重构。以下是部分代码

class TXArray extends ArrayObject
{
    private $storage = [];
    private $encodes = [];

    public function __construct($storage=array())
    {
        $this->storage = $storage;
    }

    public function getIterator()
    {
        foreach ($this->storage as $key => $value){
            $key = $this->encode($key);
            if (!isset($this->encodes[$key])){
                $this->encodes[$key] = $this->encode($value);
            }
        }
        return new ArrayIterator($this->encodes);
    }

    public function offsetGet($k)
    {
        if (isset($this->storage[$k])){
            $key = $this->encode($k);
            if (!isset($this->encodes[$key])){
                $this->encodes[$key] = $this->encode($this->storage[$k]);
            }
            return $this->encodes[$key];
        }
        return null;
    }

    public function offsetExists($k)
    {
        return isset($this->storage[$k]);
    }

    public function offsetUnset($k)
    {
        unset($this->storage[$k]);
        $k = $this->encode($k);
        unset($this->encodes[$k]);
    }

    public function offsetSet($k, $value)
    {
        $this->storage[$k] = $value;
        $this->encodes[$k] = $this->encode($value);
    }

    public function count()
    {
        return count($this->storage);
    }

    private function encode($value)
    {
        if (is_string($value)){
            $value = is_string($value) ? htmlspecialchars($value, ENT_QUOTES | ENT_SUBSTITUTE) : $value
        } elseif (is_array($value)){
            $value = new self($value);
        }
        return $value;
    }
}

offsetGet 会在$array[$key] 时候被调用。getIterator() 方法则是在 foreach循环时被调用。当发现内部参数是个array时,会再次递归调用自己,重复上述步骤。效果如下图所示:

这样一个递归的转义模型就写好了。也实现了用到时才转义的目标。

但是还有个问题。并不是所有字段都需要转义的,例如我们平台的舆情监控数据,数据来源主要是各大贴吧论坛,数据本身包含了图片img,字体颜色等html元素。在展示时并不希望被模版转义。所以我在框架上继续优化。添加了PHP的魔法方法__get()

    public function __get($k)
    {
        return isset($this->storage[$k]) ? $this->storage[$k] : null;
    }

    public function get($key)
    {
        return $this->__get($key);
    }

也就是说只要调用$array->key 或者 $array->get(0) 就可以直接获取原来的数据而不进行转义了。

另外看业务也再需要加上一些对array的处理方法,例如array_key_exists,in_array, join等。或者直接使用__call() 魔法方法

    public function __call($method, $args)
    {
        $args[] = &$this->storage;
        return call_user_func_array($method, $args);
    }

    public function serialize()
    {
        return serialize($this->storage);
    }

    public function __invoke()
    {
        return $this->storage ? true : false;
    }

    public function keys()
    {
        return array_keys($this->values(false));
    }

然后我们在页面模版里就可以愉快的使用了

但是这个TXArray还是有个问题,就是如果需要转化成json全部下发给js使用的话,那里面的数据就无法被转义了。当然也可以递归先全转义一遍,但总觉得代码不够漂亮。这个问题我还会继续研究。有新的进展和优化我都会上传到我的 PHP开源组件框架中,大家有什么好的建议都可以rtx跟我探讨沟通哈

本文由腾讯WeTest团队提供,更多资讯可直接戳链接查看:http://wetest.qq.com/lab/

微信号:TencentWeTest

时间: 2024-08-30 10:58:25

XSS注入我也不怕不怕啦——PHP从框架层面屏蔽XSS的思考和实践的相关文章

xss注入方法及验证方法

注:本文描述的是一般情况的xss注入方法及验证方法,并无覆盖所有xss情况, 步骤1:在任一输入框中输入以下注入字符 >"'><script>alert(XSS)</script> >"'><img src="javascript:alert(123456)"> 1234<%00script>alert("123456")</script> &{alert

web安全之XSS注入

之前在做项目的时候有遇到一些安全问题,XSS注入就是其中之一 那么,什么是XSS注入呢? XSS又叫CSS (Cross Site Script) ,跨站脚本攻击.它指的是恶意攻击者往Web页面里插入恶意脚本代码,而程序对于用户输入内容未过滤,当用户浏览该页之时,嵌入其中Web里面的脚本代码会被执行,从而达到恶意攻击用户的特殊目的. 跨站脚本攻击的危害:窃取cookie.放蠕虫.网站钓鱼 ... 跨站脚本攻击的分类主要有:存储型XSS.反射型XSS.DOM型XSS XSS漏洞是Web应用程序中最

Java Filter过滤xss注入非法参数的方法

http://blog.csdn.NET/feng_an_qi/article/details/45666813 Java Filter过滤xss注入非法参数的方法 web.xml: [html] view plain copy <filter> <filter-name>XSSFiler</filter-name> <filter-class> com.paic.mall.web.filter.XssSecurityFilter </filter-c

StringEscapeUtils的常用使用,防止SQL注入及XSS注入

StringEscapeUtils类可以对html js xml  sql 等代码进行转义来防止SQL注入及XSS注入 添加依赖 <dependency> <groupId>commons-lang</groupId> <artifactId>commons-lang</artifactId> <version>2.6</version> </dependency> 1.html脚本 escapeHtml转义h

依赖注入[4]: 创建一个简易版的DI框架[上篇]

本系列文章旨在剖析.NET Core的依赖注入框架的实现原理,到目前为止我们通过三篇文章(<控制反转>.<基于IoC的设计模式>和< 依赖注入模式>)从纯理论的角度对依赖注入进行了深入论述,为了让读者朋友能够更好地理解.NET Core的依赖注入框架的设计思想和实现原理,我们创建了一个简易版本的DI框架,也就是我们在前面文章中多次提及的Cat.我们会上下两篇来介绍这个被称为为Cat的DI框架,上篇介绍编程模型,下篇关注设计实现.[源代码从这里下载] 目录一.DI容器的层

依赖注入[5]: 创建一个简易版的DI框架[下篇]

为了让读者朋友们能够对.NET Core DI框架的实现原理具有一个深刻而认识,我们采用与之类似的设计构架了一个名为Cat的DI框架.在<依赖注入[4]: 创建一个简易版的DI框架[上篇]>中我们介绍了Cat的基本编程模式,接下来我们就来聊聊Cat的设计和实现. 目录一.服务注册:ServiceRegistry 二.DI容器:Cat 三.扩展方法 一.服务注册:ServiceRegistry 由于作为DI容器的Cat对象总是利用预先添加到服务注册来提供对应的服务实例,所以服务注册至关重要.如下

利用XSS注入漏洞能对网站做什么

或许大家经常看一些高手测试XSS漏洞都是alert一个窗口来.便以为XSS即是如此,当自己alert出窗口来,便说自己发现了漏洞. 其实远没这么简单.你发现的只是程序员的一个小bug而已,远谈不上XSS.它们的关系如同系统漏洞与漏洞利用的关系,不可同日而语.你的系统是否出现过“****内存不能为read”?你是否知道它是溢出漏洞的一种表现呢?但想因此做出exploit远不是那么简单的事情,你能说自己发现了溢出漏洞了么? XSS高手们的那些XSS,大家都是可以拿出来做些什么的,这样的东西才能算得上

防止SQL注入和XSS注入的方法总结

1.在OpenResty中添加naxsi加强防御 https://github.com/nbs-system/naxsi 安装方法 https://www.cnblogs.com/kgdxpr/p/9841456.html 2.防止SQl注入的思路和方法 1.永远不要信任用户的输入.对用户的输入进行校验,可以通过正则表达式,或限制长度:对单引号和 双"-"进行转换等. 2.永远不要使用动态拼装SQL,可以使用参数化的SQL或者直接使用存储过程进行数据查询存取. 3.永远不要使用管理员权

xss注入

一 HTML标签之间的注入 (<div>[输出]</div>); 最常用着input标签或者url中 ①比如弹幕 通过input获取输入的值,然后在生成li标签插入文档中,这种情况如果用的是原生的innerHTML,则对于的script标签不起作用,因为浏览器对插入body中的内容作了HTML entity编码,所以像插入类型的 <script>alert(1)</script>并不起作用,除非你自己新建script标签,再插入,则其作用.但如果你用的是jQ