下面是摘自thinkphp官方的一个公告,官方直接贴出这些东西是非常不负责的行为,跟上次apache公开的Struts2的代码执行一样的行为,会造成很多用户被黑。建议类似的厂商不要再做这种蠢事。; \2 e/ K+ d) u% o6 m6 D! R- C
ThinkPHP 3.1.3及之前的版本存在一个SQL注入漏洞,漏洞存在于ThinkPHP/Lib/Core/Model.class.php 文件" n" W! J' s6 ^/ u' f( l# \
根据官方文档对”防止SQL注入”的方法解释(见http://doc.thinkphp.cn/manual/sql_injection.html)
) ^9 J5 C& }2 I4 ~# u6 C4 R使用查询条件预处理可以防止SQL注入,没错,当使用如下代码时可以起到效果:" N' c' A+ P" z/ i5 L* g
$Model->where("id=%d and username='%s' and xx='%f'",array($id,$username,$xx))->select();% i4 D8 r: Y \+ P- M
2 l) V& E- A6 J7 m 或者+ o5 P. }2 r/ s" F0 v$ P' G
$Model->where("id=%d and username='%s' and xx='%f'",$id,$username,$xx)->select();
$ q( T, q5 ?: E) @; O5 F4 S
# y5 P) i* m/ d8 K& d 但是,当你使用如下代码时,却没有”防止SQL注入”效果(而官方文档却说可以防止SQL注入):
, a# n' `: v/ j5 V M; ~$model->query('select * from user where id=%d and status=%s',$id,$status);
2 L1 L3 B' Y1 y3 f- I: F
/ _/ g6 o3 ]2 p. b) k5 r或者5 ] J/ p5 g6 a8 G; M8 Q2 V7 Z6 W, Z
$model->query('select * from user where id=%d and status=%s',array($id,$status));
7 ~* M2 D: [1 z8 E
( H+ `0 @; |7 j$ R 原因:
, C& }) W" R1 C/ ]6 _3 w& s$ U4 MThinkPHP/Lib/Core/Model.class.php 文件里的parseSql函数没有实现SQL过滤.
7 v7 e) U0 S b! l原函数:$ b- [1 I% G, ?+ A7 u. A" n( ~" t
protected function parseSql($sql,$parse) {
; H4 ]! g+ L3 `. M( z // 分析表达式# ?; ?0 V1 M" c. ]' j
if(true === $parse) {, C' W4 u7 Y1 \' b7 A7 I# f/ v
$options = $this->_parseOptions();6 k/ c" b, i9 ^! g8 J7 o
$sql = $this->db->parseSql($sql,$options);+ m# j1 m0 `0 t3 @
}elseif(is_array($parse)){ // SQL预处理
4 `$ Z3 f% [9 W $sql = vsprintf($sql,$parse);, O3 }# ]; E# R. u0 e
}else{! Q' ^/ J9 B% B) ~
$sql = strtr($sql,array('__TABLE__'=>$this->getTableName(),'__PREFIX__'=>C('DB_PREFIX')));/ j/ [( r% w6 l: Q
}
/ @* i, L/ \. B: a $this->db->setModel($this->name);
7 M$ o$ E% R2 r. ~( n return $sql;$ `9 _ b2 f5 O1 @# {* v& M* r; Y
}
# w5 z& U+ b. _- Y2 s4 E8 _/ L$ l+ U, ]7 O
验证漏洞(举例):& c6 f8 O/ o+ }6 z0 ~
请求地址:
- ]" b, L( M- u+ j8 }1 Whttp://localhost/Main?id=boo” or 1=”1
9 [, Y ~& w* _3 I7 L$ e o或9 V6 F) I+ [ Z/ e+ x& o7 s
http://localhost/Main?id=boo%22%20or%201=%221
! q! M4 J; I. Vaction代码:
* L) d; V( B- w0 ]; f) Z6 l5 c( F$model=M('Peipeidui');( m4 g* c; }$ C9 [5 a: }3 I& h( ?
$m=$model->query('select * from peipeidui where name="%s"',$_GET['id']);+ I" D3 Y- P i& J) |# P* ]; r8 F( S* M
dump($m);exit;* j$ i6 i# P: g- f+ H6 Y( R
或者
$ X; N0 {. [+ Y1 w- h( @) v$model=M('Peipeidui');
% c; D, G1 _2 k+ i! z6 j $m=$model->query('select * from peipeidui where name="%s"',array($_GET['id']));
4 m( X6 d/ o. y- [" L3 V/ y2 Y3 [ dump($m);exit;. ]6 `" v/ F" k
结果:. h- a5 g: z7 {9 b: f7 ^. X: d6 ^
表peipeidui所有数据被列出,SQL注入语句起效.
, x& K( x# }: `' n7 D解决办法:1 [! b6 U* ^9 v, Y1 g8 L, M* v8 C0 L
将parseSql函数修改为:. O9 k5 \1 ^/ A
protected function parseSql($sql,$parse) {! z h8 r7 b) x) W( E# P4 [' Q* R
// 分析表达式, u: Z: {$ y# O. h$ y, S' j! J! ?
if(true === $parse) {
% n3 G6 m1 f+ W) m+ i5 \ $options = $this->_parseOptions();$ q( M2 @* ]( @$ r1 @4 d
$sql = $this->db->parseSql($sql,$options);5 d$ L+ O6 r& N2 U n+ F1 {
}elseif(is_array($parse)){ // SQL预处理
' T9 E! w+ o" q! S4 ~" R$ A. F- s $parse = array_map(array($this->db,'escapeString'),$parse);//此行为新增代码
9 T5 i9 Y& c5 ^) }3 m $sql = vsprintf($sql,$parse);
7 r- {" Z7 f; I' L" ~1 k }else{
% O* q r" X* W! g5 Y $sql = strtr($sql,array('__TABLE__'=>$this->getTableName(),'__PREFIX__'=>C('DB_PREFIX')));
/ A% V0 R. R% s5 G }
; H2 V' U# {8 [5 W6 D T $this->db->setModel($this->name);/ J: i4 {2 L; O1 v: B, z T9 W6 K
return $sql;
) ~ Y- S5 h2 \! | }. o: d- {* t w) c P0 Q5 F7 U9 l
& r$ S4 n* Q; @3 R总结:3 i o, p- ?6 p. `$ w( Z% K9 `
不要过分依赖TP的底层SQL过滤,程序员要做好安全检查
9 p: g8 V& b0 R" f: q" o. m; D& t不建议直接用$_GET,$_POST
( A' w; {9 ~. W2 S[/td][/tr]
0 |3 P8 l2 A4 {8 E+ i8 y[/table]+12 O, i& [- y" f
5 C, U! Q6 O( F A
4 |% [, E% R* a; k
|