下面是摘自thinkphp官方的一个公告,官方直接贴出这些东西是非常不负责的行为,跟上次apache公开的Struts2的代码执行一样的行为,会造成很多用户被黑。建议类似的厂商不要再做这种蠢事。. K8 l2 @# o. r3 u" S
ThinkPHP 3.1.3及之前的版本存在一个SQL注入漏洞,漏洞存在于ThinkPHP/Lib/Core/Model.class.php 文件. S2 x& U2 F( J) k l: L, y, R; c0 a
根据官方文档对”防止SQL注入”的方法解释(见http://doc.thinkphp.cn/manual/sql_injection.html)
- e2 R' k2 U% g2 F使用查询条件预处理可以防止SQL注入,没错,当使用如下代码时可以起到效果:) ^9 I) b) N' c" l( K$ S( }
$Model->where("id=%d and username='%s' and xx='%f'",array($id,$username,$xx))->select();
2 [1 d4 T: x2 |7 P# G6 u* S) G0 v: ^" ]' a8 L" z5 X9 w
或者
( I/ S3 g. {0 ~- R2 ]/ e$Model->where("id=%d and username='%s' and xx='%f'",$id,$username,$xx)->select();
, ~1 o& s' D9 s! N% x
- v& c7 q- [; o4 E% l; v: n- n 但是,当你使用如下代码时,却没有”防止SQL注入”效果(而官方文档却说可以防止SQL注入):
& u1 d X8 A, F+ t2 B+ e0 @$model->query('select * from user where id=%d and status=%s',$id,$status);: |! S: \% {3 ?' N
! m( O( j/ _0 j- g) d6 f: S
或者
5 Z b- h) R' D1 ]1 E- w( d/ F$model->query('select * from user where id=%d and status=%s',array($id,$status));4 R; b2 W Q1 m4 a. ^7 [( ?' x) s! l
' }8 t7 r) F/ D. ^4 {
原因:
# d7 S- u* A" j( g! VThinkPHP/Lib/Core/Model.class.php 文件里的parseSql函数没有实现SQL过滤.- u) n5 g' E) U5 p# e3 J
原函数:
( W9 ?1 ~: J; Xprotected function parseSql($sql,$parse) {. Y9 h% q/ N5 e) r% F0 {
// 分析表达式9 q* n! O( C# p4 \. D. K" G
if(true === $parse) {
4 c9 T9 _ N: V8 Q/ K6 X, K $options = $this->_parseOptions();3 Q' P" Z) S! V( Q* {" w
$sql = $this->db->parseSql($sql,$options);
c8 @/ n. d Q* Z }elseif(is_array($parse)){ // SQL预处理" X4 r* X1 \' a
$sql = vsprintf($sql,$parse);% o2 y1 a V6 {9 z P' S C5 F
}else{5 i$ e2 d7 }# M9 O
$sql = strtr($sql,array('__TABLE__'=>$this->getTableName(),'__PREFIX__'=>C('DB_PREFIX')));
" e0 ^; @3 I6 {7 M }
5 d; s4 Y+ [1 c: m! P1 a) Z $this->db->setModel($this->name);
+ f& b) J5 P/ c4 v# k1 K+ c return $sql;
- |" B2 A/ q/ {1 y+ j }# q6 s1 Y. _* o" f- L7 \0 y- l1 K; G
8 T. d7 v0 b: x& U1 E验证漏洞(举例):5 W( d, F* C" N6 j% c# Q9 o
请求地址:
2 b: n7 H! e* O/ r7 n. a1 M! fhttp://localhost/Main?id=boo” or 1=”1
$ e, o) w# Z% o或
4 f2 \3 }, _2 y- R; P- shttp://localhost/Main?id=boo%22%20or%201=%221
5 K! g* o& L. V1 g5 J6 k" m; Gaction代码:
$ E" V8 a! B1 a" w' `7 I* H$model=M('Peipeidui');
7 A9 ^' n |( j+ V5 ^- U $m=$model->query('select * from peipeidui where name="%s"',$_GET['id']);6 v# h; s6 I; U, e! v5 D2 z: K
dump($m);exit;
# Q) k0 I( w0 I+ l0 g. `或者% n% p- ]& Z0 W8 S
$model=M('Peipeidui');
$ c, q# @+ j. R4 o2 ]3 C $m=$model->query('select * from peipeidui where name="%s"',array($_GET['id']));, |( G0 |" ]' A
dump($m);exit;, v. p$ E! W' t0 F) y) f. s
结果:) Y: j, G, ^4 P6 P
表peipeidui所有数据被列出,SQL注入语句起效.
) Y! c4 v3 r4 l, @3 u! }+ J5 J6 z解决办法:
3 i4 b( V. u" `. a' t" M将parseSql函数修改为:& h$ D$ W3 Q/ u4 O! W# [
protected function parseSql($sql,$parse) {( h3 S. h% Z5 u0 H/ {/ q
// 分析表达式
) C% P7 U+ M6 l# | if(true === $parse) {
9 ]# T/ a' m6 U5 Z1 D; I $options = $this->_parseOptions();
" R. x' x0 h7 `; W }, q% v" Q: J $sql = $this->db->parseSql($sql,$options);
$ h% I. n% ?& V6 i n: v5 p }elseif(is_array($parse)){ // SQL预处理' O3 E2 ^" q* O
$parse = array_map(array($this->db,'escapeString'),$parse);//此行为新增代码& f3 z- |6 n9 R1 Q3 F
$sql = vsprintf($sql,$parse);
: [: Y! L7 F9 v" z* f2 h: Q: y }else{1 k8 d; a6 }0 Y$ r s) E; G
$sql = strtr($sql,array('__TABLE__'=>$this->getTableName(),'__PREFIX__'=>C('DB_PREFIX')));' C5 i4 T" w4 z' \1 q
}4 S& Q1 F2 S6 f: U) N
$this->db->setModel($this->name);
6 O) @5 I; [$ D$ G# v return $sql;
- Q+ }% b; Y2 M* w/ U7 g( c r }. x3 q' R' n2 x- M+ d: y, \6 [: P
3 S- k9 \2 ^% O0 N' ^% w" ^总结:
: z; F2 Q0 k! O1 n" p% p不要过分依赖TP的底层SQL过滤,程序员要做好安全检查, z) r4 `' [$ n# I+ B
不建议直接用$_GET,$_POST+ \' F+ ^4 x2 C" m' Z w! ]
[/td][/tr]
* Q, T9 o# R, O/ B' e( R[/table]+1% g; y: K8 b# v
/ f# x M4 M4 v4 E+ c* C) Y# F5 k, ^2 {! D: ^6 E! A
|