下面是摘自thinkphp官方的一个公告,官方直接贴出这些东西是非常不负责的行为,跟上次apache公开的Struts2的代码执行一样的行为,会造成很多用户被黑。建议类似的厂商不要再做这种蠢事。3 m& R) V" P7 F7 X5 [2 X# x
ThinkPHP 3.1.3及之前的版本存在一个SQL注入漏洞,漏洞存在于ThinkPHP/Lib/Core/Model.class.php 文件
7 j" A3 {- j0 C2 k+ W根据官方文档对”防止SQL注入”的方法解释(见http://doc.thinkphp.cn/manual/sql_injection.html)
1 j. v$ \! T% U9 t3 ~/ i4 o使用查询条件预处理可以防止SQL注入,没错,当使用如下代码时可以起到效果:
- h. i0 d$ D2 U, m$Model->where("id=%d and username='%s' and xx='%f'",array($id,$username,$xx))->select();* L6 M7 s8 M; h- ], X/ _
) g/ D1 A0 ?; P$ [' _. v7 ~. ^2 g4 E
或者
, b& G: i4 }& {$Model->where("id=%d and username='%s' and xx='%f'",$id,$username,$xx)->select();
. s4 ~8 x" K& \2 u: w7 B2 r6 B0 X" T# o; E8 `" D
但是,当你使用如下代码时,却没有”防止SQL注入”效果(而官方文档却说可以防止SQL注入):
5 V V7 [1 ]9 Z7 `2 x$model->query('select * from user where id=%d and status=%s',$id,$status);
8 o! s h6 N L* h% v! M3 y- A3 t$ i# W' c) u8 z& O2 V0 `( n
或者
" Y3 K# O! J; T2 `% Z' z$model->query('select * from user where id=%d and status=%s',array($id,$status));
2 r5 ]2 p% C, w7 a+ [. w3 |. I$ D! o- x: N2 F3 ?+ C
原因:
8 h; j7 G7 N* O. |ThinkPHP/Lib/Core/Model.class.php 文件里的parseSql函数没有实现SQL过滤.
- U7 \4 p4 D4 ?原函数:+ G9 \0 h9 N2 c, d% }7 {: o
protected function parseSql($sql,$parse) {
' v& _" l6 P. p8 r' E5 f // 分析表达式
3 J/ x& v- M4 n' o! ^7 G if(true === $parse) {& z0 e, A: t0 H7 ]/ T1 n* ^: y
$options = $this->_parseOptions();
- @( [. N1 P6 p $sql = $this->db->parseSql($sql,$options);
; o9 r+ a+ \) d* x8 V& S }elseif(is_array($parse)){ // SQL预处理0 z9 _6 P* ?8 R9 A& \% b2 x' F }# F
$sql = vsprintf($sql,$parse);
( J4 ]2 q3 ^+ e }else{
. d9 T* ^( g+ e) j) l0 v. {3 a $sql = strtr($sql,array('__TABLE__'=>$this->getTableName(),'__PREFIX__'=>C('DB_PREFIX')));: W" E; J% A% \. T
}
3 o j# b" e# S2 F) f# L4 Q $this->db->setModel($this->name);
4 B, s; X) Y* L' t) m3 o return $sql;! K3 n/ G3 l, l2 E3 ]
}( T" z3 S# i7 n( @$ q K; h
# c5 I( m+ m/ H4 x
验证漏洞(举例):- H0 {6 a5 y# Z Y; A; {
请求地址:
" c% P" X6 H0 }7 }& y: ^# Jhttp://localhost/Main?id=boo” or 1=”1
9 q/ p1 p( M) f6 p6 y% O或& s1 u5 k, v2 J( y# Y7 t k U4 M% n
http://localhost/Main?id=boo%22%20or%201=%2219 W1 s% C5 j; v* x7 H( t$ X7 Z& L
action代码:, O* Y; y4 C9 [: t$ f+ Q( h7 m
$model=M('Peipeidui');
* x2 A* W# E* g7 d+ h8 x( j $m=$model->query('select * from peipeidui where name="%s"',$_GET['id']);. k$ J! _. H$ i" D1 }; H
dump($m);exit;
. {. Z% \) z2 \! X/ ]- p$ e或者+ E( V0 O; U4 v+ q, ]# K
$model=M('Peipeidui');
3 g0 F2 ~/ V' p) ] $m=$model->query('select * from peipeidui where name="%s"',array($_GET['id']));
- { i5 e( }5 Q* ^' s: r1 l0 u4 f dump($m);exit;
6 W3 u6 f4 V; `1 q' O9 h1 M结果:4 h5 w2 p8 ]+ X8 g' o
表peipeidui所有数据被列出,SQL注入语句起效.7 ?- z2 p; l* {' n% q3 B7 ~
解决办法:
6 c4 ^9 i6 {( ^2 i% p' D: ^& T将parseSql函数修改为:
) X* q4 W) t2 ?& s8 O2 |protected function parseSql($sql,$parse) {
6 T. T) V, J- K0 ^6 y" K6 K- z" t8 V // 分析表达式% C0 O+ \6 u2 P# B; x7 }% _# R7 A1 F
if(true === $parse) {$ |3 I, ~$ S- v. @ W9 j* _
$options = $this->_parseOptions();
( v9 }# z: o/ @. N3 ?$ i $sql = $this->db->parseSql($sql,$options);
n! l0 t' I7 C) Q! v2 P: p }elseif(is_array($parse)){ // SQL预处理8 H7 W% J" T8 D) ^2 }# U
$parse = array_map(array($this->db,'escapeString'),$parse);//此行为新增代码, R3 ^2 q$ C3 x3 G. X# s
$sql = vsprintf($sql,$parse);: y- @6 m1 U, q: d
}else{4 L5 {! o4 x8 D* K" I% Z
$sql = strtr($sql,array('__TABLE__'=>$this->getTableName(),'__PREFIX__'=>C('DB_PREFIX')));
' F! _' D1 e4 b7 H, r# I }# L1 n7 G5 B+ O* _$ L) m! g
$this->db->setModel($this->name);
1 h1 d. W( z7 Z* i1 F/ n; { return $sql;% |/ w. ], t! p* X
} u8 l7 d3 s6 n1 K* K5 w
( }2 O* i3 p0 K9 u3 J$ L' d4 H总结:9 k) _! v w+ s& e3 L6 f4 ^
不要过分依赖TP的底层SQL过滤,程序员要做好安全检查' l: r8 h9 F" G& \8 p! P
不建议直接用$_GET,$_POST
/ S- O8 R6 v5 t, c[/td][/tr]
% W2 I i' S1 w- Z" Z[/table]+1
$ Y8 ^2 D0 d9 s+ b7 c3 o% `+ `; m3 j& q% V. {( ]6 I- L
8 h U, J4 g9 R |