下面是摘自thinkphp官方的一个公告,官方直接贴出这些东西是非常不负责的行为,跟上次apache公开的Struts2的代码执行一样的行为,会造成很多用户被黑。建议类似的厂商不要再做这种蠢事。8 ~# {8 v6 }, D/ M$ R) Y
ThinkPHP 3.1.3及之前的版本存在一个SQL注入漏洞,漏洞存在于ThinkPHP/Lib/Core/Model.class.php 文件: {- b8 R( t" Z0 c6 R
根据官方文档对”防止SQL注入”的方法解释(见http://doc.thinkphp.cn/manual/sql_injection.html)( U) C- M+ ^) Y5 O# \
使用查询条件预处理可以防止SQL注入,没错,当使用如下代码时可以起到效果:
6 T: c1 i8 B+ ]" J( I$Model->where("id=%d and username='%s' and xx='%f'",array($id,$username,$xx))->select(); G' b& `! I! B: v* T
5 E' ^* f2 \& n7 i 或者# ]; ]# n" p' [4 P! W8 E. o$ `. |& T
$Model->where("id=%d and username='%s' and xx='%f'",$id,$username,$xx)->select();8 H: X/ E* B6 { u: e P+ H
# Q& {! G! T6 t* l) K
但是,当你使用如下代码时,却没有”防止SQL注入”效果(而官方文档却说可以防止SQL注入):
9 s7 \' ~6 E0 B: t0 R$ l! u% _$model->query('select * from user where id=%d and status=%s',$id,$status);; s a: R9 P; b0 y8 P0 A. L4 F
6 X9 I4 O1 [, O1 Q# W( Q1 F3 h/ B0 i
或者' T8 y% a+ G2 p/ v5 Y. B
$model->query('select * from user where id=%d and status=%s',array($id,$status));
8 o2 q) U8 E5 l1 G. V( P! y
5 q/ d9 s f7 p% R9 t 原因:* i8 l6 Y0 F2 n8 e) R8 T1 |
ThinkPHP/Lib/Core/Model.class.php 文件里的parseSql函数没有实现SQL过滤.3 r0 ?: l, {% T5 |7 ]
原函数:
3 H6 ]/ X2 k* e3 Q/ X3 ^protected function parseSql($sql,$parse) {
& p a& A! y6 T1 ^; S$ n // 分析表达式% T6 {" j( n6 o+ U) f
if(true === $parse) {
; O4 d/ [0 d' E# ?6 \* V $options = $this->_parseOptions();2 L/ u4 l8 x( F/ D8 X
$sql = $this->db->parseSql($sql,$options);
& t% G" _8 w7 N9 i% c5 H$ t }elseif(is_array($parse)){ // SQL预处理2 ]- `! `# o/ Q, @
$sql = vsprintf($sql,$parse);
1 ~" _. j" }9 Z }else{
( u" O1 y& {1 y8 x3 w: L $sql = strtr($sql,array('__TABLE__'=>$this->getTableName(),'__PREFIX__'=>C('DB_PREFIX')));% i& A( S- Y" v* ? n# H0 ]
}( M& A: Y$ |) J1 q
$this->db->setModel($this->name);) V) W l5 [' D2 L
return $sql;$ Z+ E* {3 X1 T+ u' x& [# g
}
! @/ _+ B& J7 Z- o- x* |& E, h4 o' k
# E" j) L2 o9 ^" ?' q# v- c验证漏洞(举例):4 V$ S. j- c9 u
请求地址:
8 b- g; A y0 n, Q4 S- E4 C. s8 Shttp://localhost/Main?id=boo” or 1=”1/ c4 M& C% ]) q9 S
或
# i9 ~) M- R( {' i3 _7 S- D3 M, Xhttp://localhost/Main?id=boo%22%20or%201=%2210 B o! E6 H w+ S
action代码:
7 D. z* _$ _4 g+ p i% J$model=M('Peipeidui');
- i. E* O) M% Q" Z- H* t* B $m=$model->query('select * from peipeidui where name="%s"',$_GET['id']);! @6 N H% D. i& J$ J' K$ g
dump($m);exit;* B0 v- F: y9 _2 Y& L2 |
或者$ {/ E4 f: H+ @4 c% F
$model=M('Peipeidui');
/ J- G) \- X& G2 w ~ $m=$model->query('select * from peipeidui where name="%s"',array($_GET['id']));
- r+ s7 @. Q' x( Y0 S' z dump($m);exit;/ ~( h+ h1 v- @1 x( h
结果:* e, H* O4 L/ K; L
表peipeidui所有数据被列出,SQL注入语句起效." J* b' n* s$ @) K7 {6 g) A0 ?
解决办法:
. Y0 w1 `. F& T: a2 i3 Z将parseSql函数修改为:; e8 ^/ Z; H$ b" l" h" z' w) e: j
protected function parseSql($sql,$parse) {
* m4 x. Z+ v9 o9 V/ `/ t2 ` // 分析表达式
, T8 b7 ]/ e2 o3 P if(true === $parse) {
0 M- i. v* K& N3 z. t $options = $this->_parseOptions();
( L P% g6 l6 a7 J) M0 m $sql = $this->db->parseSql($sql,$options);! y* @ G0 C% g/ e6 @$ B1 o
}elseif(is_array($parse)){ // SQL预处理
* G- p \9 J2 ?& M: z $parse = array_map(array($this->db,'escapeString'),$parse);//此行为新增代码1 v" A6 X3 m8 u) n' j
$sql = vsprintf($sql,$parse);$ J2 I+ O' S2 F1 y
}else{
5 S6 h% C; N$ H9 m$ T x9 r $sql = strtr($sql,array('__TABLE__'=>$this->getTableName(),'__PREFIX__'=>C('DB_PREFIX')));" U S7 B5 H" c
}
3 S( Y( |3 V' G: |& a $this->db->setModel($this->name);+ _1 c# r$ }: F( g4 V; g$ Y/ J5 {
return $sql;
" o. r& E1 g/ ~% f: D2 j8 L+ q }
. ]) s$ L* t" ]! {- b! o: v2 S6 I* {; K/ g& p
总结:% `: x+ l8 ~3 O$ u6 u8 j
不要过分依赖TP的底层SQL过滤,程序员要做好安全检查# Y' k1 p7 |" ]. B5 D4 T+ V% E. t5 a
不建议直接用$_GET,$_POST
. [! y8 k: E* Y* y* z' q4 {[/td][/tr], k- W8 q6 n% M8 u
[/table]+1) O* a/ p+ u' f& g
+ `1 _: w9 _9 O5 v2 z
0 y1 D. `7 {, Z) L" N3 x8 @ |