下面是摘自thinkphp官方的一个公告,官方直接贴出这些东西是非常不负责的行为,跟上次apache公开的Struts2的代码执行一样的行为,会造成很多用户被黑。建议类似的厂商不要再做这种蠢事。( N' ]9 i- Q. c1 I9 w; y
ThinkPHP 3.1.3及之前的版本存在一个SQL注入漏洞,漏洞存在于ThinkPHP/Lib/Core/Model.class.php 文件
+ j5 {. C- s+ [2 t ~3 D" ]根据官方文档对”防止SQL注入”的方法解释(见http://doc.thinkphp.cn/manual/sql_injection.html)8 J% b. i1 F' b s6 [0 ]: P8 T
使用查询条件预处理可以防止SQL注入,没错,当使用如下代码时可以起到效果:
1 y) b0 f' M7 q0 F( `9 V) @$Model->where("id=%d and username='%s' and xx='%f'",array($id,$username,$xx))->select();7 h- t6 n1 K; x2 s0 c B3 j
, g- e; ~% ?, z4 ]) a+ ~ 或者6 z- w% j$ Y b7 }/ N
$Model->where("id=%d and username='%s' and xx='%f'",$id,$username,$xx)->select();
! Y, M8 d4 ?, X
, ~; f! e2 L0 ^/ n/ @2 g 但是,当你使用如下代码时,却没有”防止SQL注入”效果(而官方文档却说可以防止SQL注入):- f2 ~# d, b7 f+ r% z+ k1 T5 C2 Q
$model->query('select * from user where id=%d and status=%s',$id,$status);/ n7 C" c6 h5 }+ L- X' J2 C
1 W7 ~4 D; _0 P" ?或者/ s/ Q7 Y- W& W$ F$ _" ?' o9 K
$model->query('select * from user where id=%d and status=%s',array($id,$status));
; F/ o1 s" c& i" i
) \( w/ P+ K g( |. O& ~$ E 原因:
# o2 X: J$ l( x# X. D; ]( \: {5 n: k: U8 lThinkPHP/Lib/Core/Model.class.php 文件里的parseSql函数没有实现SQL过滤.5 B4 C, E- ^- b: R
原函数:0 U# X9 B3 k' {5 |( Z
protected function parseSql($sql,$parse) {5 |) Z4 V' {! V1 p. B( S J
// 分析表达式6 ^$ L! r, L, u5 x& c" n3 [8 }
if(true === $parse) {
5 T- _4 P5 ^. F5 `" F2 w$ ? } _ $options = $this->_parseOptions();
4 D, M; T& N; ^: W) o# p: Q6 \& ` $sql = $this->db->parseSql($sql,$options);& L4 g- s9 P; b2 F& Z' l. o
}elseif(is_array($parse)){ // SQL预处理
% g. v9 ^, H* R" ^2 Z. J8 r; d $sql = vsprintf($sql,$parse);
2 ~9 {. l* S, R2 f/ v }else{4 v8 l5 R* W3 `* B5 \6 l3 D2 |. _! U
$sql = strtr($sql,array('__TABLE__'=>$this->getTableName(),'__PREFIX__'=>C('DB_PREFIX')));# U/ W1 a3 K7 W" P! c
}
; Y! `1 |2 F6 Y/ ^5 k5 I3 ]) X $this->db->setModel($this->name); _4 z- w9 M& i. q
return $sql;% V" ]: e0 `6 p+ P, m$ `2 K
}
% d0 E, p, h6 e, q ?
: R s* Q& r" P7 l2 j验证漏洞(举例):( j# R- v' w, b0 _+ d! t
请求地址:& r A H1 D0 a. M0 Z
http://localhost/Main?id=boo” or 1=”1
6 f3 W6 V( ^) q9 r" L: \" p或3 R. M' d G! H' X
http://localhost/Main?id=boo%22%20or%201=%221+ p0 r# F6 \- h
action代码:0 z6 m4 V b" |8 @7 r. H& _
$model=M('Peipeidui');
( z7 U. M' m, g* ^9 E$ | $m=$model->query('select * from peipeidui where name="%s"',$_GET['id']);3 t, o8 ?+ I8 m+ D! K8 p: t/ \- i
dump($m);exit;
8 w8 v; |7 N3 R% y7 T或者
6 S: U7 S1 q; V& {$model=M('Peipeidui');
9 r) x, g! Z0 I6 Q, i' V( m; V $m=$model->query('select * from peipeidui where name="%s"',array($_GET['id']));: h$ j7 n0 Y1 K; x! y% o4 T
dump($m);exit;
) }0 D7 F1 E* R结果:
+ m& z+ c- n3 }' a' {3 w) R8 @表peipeidui所有数据被列出,SQL注入语句起效.5 v7 }- [ K. d4 f: R1 G
解决办法:
) X) ^9 ]5 r m q+ k# G6 M将parseSql函数修改为:
; m! @2 Z7 M, C5 tprotected function parseSql($sql,$parse) {7 w, j& z5 ]5 d- I# I
// 分析表达式
9 v/ i! {2 P$ v: t if(true === $parse) {! h/ m$ ^2 Y$ t- z" v! [
$options = $this->_parseOptions();5 b" e+ y1 t: @5 w* ]
$sql = $this->db->parseSql($sql,$options);
' e8 V" p8 w- h9 m6 B1 b }elseif(is_array($parse)){ // SQL预处理2 M$ c: u9 S) \: X
$parse = array_map(array($this->db,'escapeString'),$parse);//此行为新增代码# L3 h3 u9 x3 n$ O Z
$sql = vsprintf($sql,$parse);
! }* V) ?. B4 w7 M1 U, R6 c }else{
: w+ k: c3 x$ L1 L, M; j8 g& w $sql = strtr($sql,array('__TABLE__'=>$this->getTableName(),'__PREFIX__'=>C('DB_PREFIX')));# P% m6 w6 P3 P# j
}3 z" R. q1 D9 p9 h
$this->db->setModel($this->name);
9 |) F" d6 N) n" ~) F return $sql;# y# n+ A) S; l9 ~
}" l0 Z$ \9 P y3 d0 p
) }9 [5 R, e; r. x& E4 T- Y
总结:% L+ a9 X0 G' \- f8 F8 i( f L
不要过分依赖TP的底层SQL过滤,程序员要做好安全检查
& e# S) x- R1 i0 R9 D& r不建议直接用$_GET,$_POST. v3 \* N: Z8 K9 O5 x
[/td][/tr]6 [& C- a/ Y2 `! {# e# B Q$ g
[/table]+1
" n8 q$ q) t( P# s8 P8 [3 g. I( x2 i u6 Q J
% I0 X. b, f _- Y |