根据leader要求,web版页面打开时间需要在1s内,对于目前项目依赖api的设计方案是一大考验。结合项目的实际情况以及YII的特性,考虑产品详情的基础数据做静态缓存,其它动态数据异步调用,根据方案进行设计,一个同事设计缓存key依赖时使用了varyByExpression,造成了命令行注入。具体排查过程如下:
1、注入表现:
http://xxx.xxxxxx.xxx/xxx/hotelPhoto?hotelid=%24%7b%40print(md5(acunetix_wvs_security_test))%7d
2、问题定位:
是在页面渲染前输出内容,考虑基类或者初始化没有做参数过滤。
3、代码追踪:
A、检查基类(父类),没有发现echo、print等输出。
B、继续检查代码,在过滤器发现如下语句:
public function filters() {
if(Yii::app()->params['allowCache']=='Y' || $_GET['nocache'] == 1){
$index_exp = $this->_gets->getParam('hotelid', 0, true);
$ycf_product_detail = Yii::app()->cache->get('2016_Product_detail');//缓存key后缀
return array (
array(
'YcfCOutputCache + hotelPhoto,navigation',
'duration' => Yii::app()->params['cacheTimeSet'],
'varyByExpression' => $index_exp,
'keySuffix' => $ycf_product_detail
),
);
}
}
$index_exp = $this->_gets->getParam('hotelid', 0, true);这里获取参数没有过滤。
C、继续查看框架缓存的代码COutputCache.php
getCacheKey()函数有如下代码:
if($this->varyByExpression!==null)
$key.=$this->evaluateExpression($this->varyByExpression);
查看父类CComponent.php 的evaluateExpression函数
public function evaluateExpression($_expression_,$_data_=array())
{
if(is_string($_expression_)){
extract($_data_);
return eval('return '.$_expression_.';');
}else{
$_data_[]=$this;
return call_user_func_array($_expression_, $_data_);
}
}
发现会直接执行输入命令,eval() 函数把字符串按照PHP 代码来计算。
4、解决方法:对获取的参数进行过滤$index_exp = (int)$this->_gets->getParam('hotelid', 0, true);