标题: ThinkPHP框架通杀所有版本的一个SQL注入漏洞 [打印本页] 作者: admin 时间: 2013-7-27 18:30 标题: ThinkPHP框架通杀所有版本的一个SQL注入漏洞 下面是摘自thinkphp官方的一个公告,官方直接贴出这些东西是非常不负责的行为,跟上次apache公开的Struts2的代码执行一样的行为,会造成很多用户被黑。建议类似的厂商不要再做这种蠢事。 - _8 B9 \3 s9 RThinkPHP 3.1.3及之前的版本存在一个SQL注入漏洞,漏洞存在于ThinkPHP/Lib/Core/Model.class.php 文件 % W" l) B/ V( O5 b根据官方文档对”防止SQL注入”的方法解释(见http://doc.thinkphp.cn/manual/sql_injection.html)4 I- C' B) ?! S
使用查询条件预处理可以防止SQL注入,没错,当使用如下代码时可以起到效果:. _9 r1 Z! F7 ~% i* A
$Model->where("id=%d and username='%s' and xx='%f'",array($id,$username,$xx))->select(); ! m( s( D$ A. L! I8 r' g- N- _+ n6 F1 g2 I' G8 U# N1 t1 ^
或者 H5 B- A8 n! ~+ b9 j$Model->where("id=%d and username='%s' and xx='%f'",$id,$username,$xx)->select();" z5 {# R9 A! D) G
) x5 P6 _7 o: K- v2 F. v3 H+ z- ^: M 但是,当你使用如下代码时,却没有”防止SQL注入”效果(而官方文档却说可以防止SQL注入):/ u. r: A* J' N9 }# _+ m/ v
$model->query('select * from user where id=%d and status=%s',$id,$status);! R, M) M. M5 T7 Z5 |. F' M; z
) v1 _2 g# Z* s& s& p9 @或者 7 G- |/ A. q0 f' ~7 J$model->query('select * from user where id=%d and status=%s',array($id,$status));4 n0 }- y3 c9 t( d
P4 @9 v: ` t: j" Z 原因: : }) w5 U- ~! c9 W. l5 ZThinkPHP/Lib/Core/Model.class.php 文件里的parseSql函数没有实现SQL过滤. . H- Z5 |- p( s F3 y原函数:$ f+ z# V/ U/ P' b. ^: T1 p/ H* X1 ~
protected function parseSql($sql,$parse) {$ m2 i0 m) B# }0 W, b$ s. s+ O
// 分析表达式 + d( d9 M6 A# Z, k, b7 ]8 H) s if(true === $parse) {/ y. p* R' s* R
$options = $this->_parseOptions(); # u+ p7 v6 o5 l7 J2 T $sql = $this->db->parseSql($sql,$options); 1 G O" v+ w+ y, L4 `" B }elseif(is_array($parse)){ // SQL预处理9 C- w. p! F/ }+ P* B% [- |' x
$sql = vsprintf($sql,$parse);3 e% |$ T- T6 \
}else{8 } r& B' o z) H; ~ h
$sql = strtr($sql,array('__TABLE__'=>$this->getTableName(),'__PREFIX__'=>C('DB_PREFIX')));# R3 Z" c8 s. t. L
} & x6 d. Y$ \4 w+ G $this->db->setModel($this->name); 6 @9 B2 W- [' S4 m" ]: D7 Q- B) u& f return $sql;1 E$ g4 \3 Y3 w% L
}4 F% Z% E- @* }; W, h$ _- e$ L
$ w# q" n+ v2 O" a
验证漏洞(举例):7 V' \0 ?% ^" F, G
请求地址: ' G( @$ [4 @* w: |7 i1 s1 |7 \: Bhttp://localhost/Main?id=boo” or 1=”1 9 F) U9 ^1 b" k# D( N或 " i2 E$ [; @8 y% l6 `. v8 `http://localhost/Main?id=boo%22%20or%201=%221$ E3 s9 f" N9 Q3 e
action代码:9 n. t4 t, `5 [4 v
$model=M('Peipeidui');, j5 J" @& _0 R
$m=$model->query('select * from peipeidui where name="%s"',$_GET['id']); , \4 E4 Z4 r( V: @9 n dump($m);exit;( n% q$ J0 g' F& t
或者 $ ~( p5 f$ V% w r$model=M('Peipeidui'); ; a; b' c9 [" N4 m1 g7 F% N: s5 D $m=$model->query('select * from peipeidui where name="%s"',array($_GET['id'])); % l& m5 r1 Z" W# j4 k; f# d- n dump($m);exit; 5 ~' e& Z' L0 R: ^2 o! M结果:9 H! A; s1 \ M# C6 t$ I
表peipeidui所有数据被列出,SQL注入语句起效.! _) S, j6 j0 W1 _& R# F
解决办法:, X" `0 ~- X9 L: v( M- ~
将parseSql函数修改为:5 u2 Z7 Q3 @" R1 n) c6 S1 O9 _6 G
protected function parseSql($sql,$parse) {* |- F, N: l# S# \
// 分析表达式( v( x7 A, K3 h x) \
if(true === $parse) {$ c5 T0 v" R; C! P% X; s& D0 W, u: ~
$options = $this->_parseOptions();" q! `( @2 W# q* Y+ y: A9 }7 ~
$sql = $this->db->parseSql($sql,$options);4 ~: N2 B8 ^/ L5 V) J# M8 h7 K% { j" _
}elseif(is_array($parse)){ // SQL预处理* ?' e' b# L) Q! K: v
$parse = array_map(array($this->db,'escapeString'),$parse);//此行为新增代码 % G: @# e' T l $sql = vsprintf($sql,$parse);( y5 R, l: S* {
}else{# ^9 O& |9 ^1 ]1 D L
$sql = strtr($sql,array('__TABLE__'=>$this->getTableName(),'__PREFIX__'=>C('DB_PREFIX')));5 @* F' S& t' O8 [
}3 B( s( S% `8 a& \# s5 a* T
$this->db->setModel($this->name); }% E# ^& F' H( F# Q0 T% v
return $sql; : Y" |) I6 R& e, D" C' d& T2 W6 D }; c: d/ Q- q0 C/ L- y/ ]: Y- h
0 v! V, `" X/ K5 y5 ]5 l
总结: % _% F8 C% c _: M9 j- c3 H& R不要过分依赖TP的底层SQL过滤,程序员要做好安全检查 * `. ?! B# @$ l: `1 ]2 C0 a不建议直接用$_GET,$_POST z- B- s" T7 F, M5 U6 f' \( W4 _[/td][/tr]+ z+ @: S9 s' W/ ^
[/table]+1 8 T% Z+ Y8 B% \( L6 R( i% f# }4 h q% m" X, r O6 x/ M