微旗子暗记: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 ([Math Processing Error]
[Math Processing Error][Math Processing Error]
}
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 [Math Processing Error]
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 会在[Math Processing Error]
这样一个递归的转义模型就写好了。也实现了用到时才转义的目标。
但是还有个问题。并不是所有字段都须要转义的,例如我们平台的舆情监控数据,数据来源紧张是各大贴吧论坛,数据本身包含了图片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);}
也便是说只要调用[Math Processing Error]
其余看业务也再须要加上一些对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跟我磋商沟通哈