下面是摘自thinkphp官方的一个公告,官方直接贴出这些东西是非常不负责的行为,跟上次apache公开的Struts2的代码执行一样的行为,会造成很多用户被黑。建议类似的厂商不要再做这种蠢事。; l- _) F4 e; X k& L
ThinkPHP 3.1.3及之前的版本存在一个SQL注入漏洞,漏洞存在于ThinkPHP/Lib/Core/Model.class.php 文件
?) D" t, U$ {2 b根据官方文档对”防止SQL注入”的方法解释(见http://doc.thinkphp.cn/manual/sql_injection.html)
8 {- h4 h6 C2 t" f使用查询条件预处理可以防止SQL注入,没错,当使用如下代码时可以起到效果:
- ~$ W7 S$ w0 b; s+ ~. Y5 x$Model->where("id=%d and username='%s' and xx='%f'",array($id,$username,$xx))->select();7 f4 {1 B' X2 E' `
s% ~1 ~9 _* i$ J8 Y 或者
# w9 B m8 v: A$Model->where("id=%d and username='%s' and xx='%f'",$id,$username,$xx)->select();9 p0 D5 |( O( V% L: I' X7 I
0 m: c/ O* n' Z+ x
但是,当你使用如下代码时,却没有”防止SQL注入”效果(而官方文档却说可以防止SQL注入):
0 `. ~; W4 ~9 z) Y! e! v, i! P$model->query('select * from user where id=%d and status=%s',$id,$status);
1 U% z7 I( P2 H* y9 a3 J/ v6 X+ h! X: h& |+ W2 ?; W) B0 r
或者
8 C# M. B# {6 W8 ^3 @$model->query('select * from user where id=%d and status=%s',array($id,$status));
- b }1 j5 H4 B) y0 K
1 ]& R9 z! b# I 原因:
# B X3 P' ~- S# K# _, R/ l- ^ThinkPHP/Lib/Core/Model.class.php 文件里的parseSql函数没有实现SQL过滤.
$ S1 F' A U* d, T% F9 D原函数:
! b! K; p X( ~0 K1 j7 i7 \" gprotected function parseSql($sql,$parse) {
5 t% N- c6 ^' e5 j* A @9 x // 分析表达式
, q# O7 T5 Z& _6 m0 }) A& J if(true === $parse) {5 N7 X! A: j! s( P( b
$options = $this->_parseOptions();% R# Y$ m- j! P, A9 R. E- U1 }
$sql = $this->db->parseSql($sql,$options);
1 x# w: k4 {, S( {, e }elseif(is_array($parse)){ // SQL预处理$ z9 ~* o! O8 Q L ]+ }' F
$sql = vsprintf($sql,$parse);
0 Q2 q$ k5 _1 e. e' B B }else{4 x4 j8 j1 r8 Q" h( y
$sql = strtr($sql,array('__TABLE__'=>$this->getTableName(),'__PREFIX__'=>C('DB_PREFIX')));- y4 j' I0 p: C2 F
}
# f% T* U) t, t& I4 c' k $this->db->setModel($this->name);- H' O8 n7 f& j, }; U# @' |
return $sql;) w7 s3 N) n- h$ U
}$ }$ A1 ]- k( x- b" }& ?
% J& j+ j' P4 X/ r2 d9 B+ X
验证漏洞(举例):- W% Z# j) T2 m" S m
请求地址:
6 ~! z9 _( {* p9 ~% I K$ X3 p% ahttp://localhost/Main?id=boo” or 1=”1
& G0 N$ l2 p$ C- f; U" S2 n; D或
0 D6 p1 R& U; p/ Phttp://localhost/Main?id=boo%22%20or%201=%221& M( R/ b* E; C! @4 y0 f$ ?5 g
action代码:
* o! ?2 B# ]$ q5 P" m1 b& e) P. Y; R$model=M('Peipeidui');. z* D* Z! F. t! I1 F$ H/ `
$m=$model->query('select * from peipeidui where name="%s"',$_GET['id']);! O; _4 t2 A' @( ^7 s) ^
dump($m);exit;* r+ E: @5 @7 Y1 B. \, E4 B& T
或者; Y' Q' L% ^# N0 M N
$model=M('Peipeidui');
* j" O2 A: w/ m) k8 I $m=$model->query('select * from peipeidui where name="%s"',array($_GET['id']));
/ f; [' h. m( A \$ a" f% o! ]7 | dump($m);exit;
) e/ `# z0 Q8 h+ S结果:8 r7 u2 u9 l2 @" \! }
表peipeidui所有数据被列出,SQL注入语句起效.0 H, D$ F) r" C4 m+ A
解决办法:
( a( M+ G1 I9 ~* i将parseSql函数修改为:
& U, z# i. r. S# Kprotected function parseSql($sql,$parse) {( P. S' s) h7 D: {% g
// 分析表达式
* ?2 ?5 z3 U- |1 j" [8 g" b. M9 | if(true === $parse) {0 O5 e$ X* ?$ r( K- K4 ?: f
$options = $this->_parseOptions();8 m, d0 P1 d$ Y$ R
$sql = $this->db->parseSql($sql,$options);% Z3 d+ C) [ R! w9 k' s
}elseif(is_array($parse)){ // SQL预处理8 e+ ^( O4 b( m0 o( n; N
$parse = array_map(array($this->db,'escapeString'),$parse);//此行为新增代码: d. p, ~, i1 j4 M5 ]
$sql = vsprintf($sql,$parse);
4 s1 y$ x, y; c% S+ T' \ }else{
. F( p1 o) D- u1 E $sql = strtr($sql,array('__TABLE__'=>$this->getTableName(),'__PREFIX__'=>C('DB_PREFIX')));
: ~" v* ^6 U+ H8 x, F) d8 h }
8 E4 K. B, E1 K- F5 Y% }3 C) w $this->db->setModel($this->name);' W% i/ c% c; M# N, A) o. D& R
return $sql;
$ g& g6 S6 {% |' d; L v- V }
& i) l$ }6 b0 M; k) M1 _6 d7 E2 ~3 p7 Q
总结:* B( j$ t5 {4 T; H0 K6 G5 e
不要过分依赖TP的底层SQL过滤,程序员要做好安全检查- L% z1 P b" V3 s) K0 B
不建议直接用$_GET,$_POST9 | ^4 _/ n( h/ Z2 |8 O# h1 `/ D
[/td][/tr]
; t# Q: I8 y' R[/table]+1, z1 S9 A3 l" X4 d1 Q6 }
4 \' E1 ^$ Q* G; m4 ~
* `4 ], P2 u( T R |