ThinkPHP官网上曾有一段公告指出,在ThinkPHP 3.1.3及之前的版本存在一个SQL注入漏洞,漏洞存在于ThinkPHP/Lib/Core/Model.class.php 文件
根据官方文档对"防止SQL注入"的方法解释(参考http://doc.thinkphp.cn/manual/sql_injection.html)
使用查询条件预处理可以防止SQL注入,没错,当使用如下代码时可以起到效果:
1
|
$Model ->where( "id=%d and username='%s' and xx='%f'" , array ( $id , $username , $xx ))->select();
|
或者
1
|
$Model ->where( "id=%d and username='%s' and xx='%f'" , $id , $username , $xx )->select();
|
但是,当你使用如下代码时,却没有"防止SQL注入"的效果(但是官方文档却说可以防止SQL注入):
1
|
$model ->query( 'select * from user where id=%d and status=%s' , $id , $status );
|
或者
1
|
$model ->query( 'select * from user where id=%d and status=%s' , array ( $id , $status ));
|
原因分析:
ThinkPHP/Lib/Core/Model.class.php 文件里的parseSql函数没有实现SQL过滤.
其原函数为:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
protected function parseSql( $sql , $parse ) {
// 分析表达式
if (true === $parse ) {
$options = $this ->_parseOptions();
$sql = $this ->db->parseSql( $sql , $options );
} elseif ( is_array ( $parse )){ // SQL预处理
$sql = vsprintf( $sql , $parse );
} else {
$sql = strtr ( $sql , array ( '__TABLE__' => $this ->getTableName(), '__PREFIX__' =>C( 'DB_PREFIX' )));
}
$this ->db->setModel( $this ->name);
return $sql ;
}
|
验证漏洞(举例):
请求地址:
1
|
http://localhost/Main?id=boo" or 1="1
|
或
action代码:
1
2
3
|
$model =M( 'Peipeidui' );
$m = $model ->query( 'select * from peipeidui where name="%s"' , $_GET [ 'id' ]);
dump( $m ); exit ;
|
或者:
1
2
3
|
$model =M( 'Peipeidui' );
$m = $model ->query( 'select * from peipeidui where name="%s"' , array ( $_GET [ 'id' ]));
dump( $m ); exit ;
|
结果:
表peipeidui所有数据被列出,SQL注入语句起效.
解决方法:
可将parseSql函数修改为:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
protected function parseSql( $sql , $parse ) {
// 分析表达式
if (true === $parse ) {
$options = $this ->_parseOptions();
$sql = $this ->db->parseSql( $sql , $options );
} elseif ( is_array ( $parse )){ // SQL预处理
$parse = array_map ( array ( $this ->db, 'escapeString' ), $parse ); //此行为新增代码
$sql = vsprintf( $sql , $parse );
} else {
$sql = strtr ( $sql , array ( '__TABLE__' => $this ->getTableName(), '__PREFIX__' =>C( 'DB_PREFIX' )));
}
$this ->db->setModel( $this ->name);
return $sql ;
}
|
总结:
1.不要过分依赖TP的底层SQL过滤,程序员要做好安全检查
2.不建议直接用$_GET,$_POST