下面是摘自thinkphp官方的一个公告,官方直接贴出这些东西是非常不负责的行为,跟上次apache公开的Struts2的代码执行一样的行为,会造成很多用户被黑。建议类似的厂商不要再做这种蠢事。
5 @# U. G* T5 A4 _ThinkPHP 3.1.3及之前的版本存在一个SQL注入漏洞,漏洞存在于ThinkPHP/Lib/Core/Model.class.php 文件
! R4 a8 ?9 k+ q9 j% c$ X \* i5 |根据官方文档对”防止SQL注入”的方法解释(见http://doc.thinkphp.cn/manual/sql_injection.html)
* p) @. ~4 ^6 u9 c H+ Q3 F4 c% W6 M使用查询条件预处理可以防止SQL注入,没错,当使用如下代码时可以起到效果:5 @: m3 Z: f6 @/ G: e+ q, o l' Y
$Model->where("id=%d and username='%s' and xx='%f'",array($id,$username,$xx))->select();
* ^* K% D- q/ {& A% g, C: R# o! J( R" | D6 u
或者
' ?3 m9 \* ]( W$Model->where("id=%d and username='%s' and xx='%f'",$id,$username,$xx)->select();
* `0 Z2 D# [9 T3 M) y# b7 ]
# k6 G) e6 Z- p 但是,当你使用如下代码时,却没有”防止SQL注入”效果(而官方文档却说可以防止SQL注入):$ b% B% ~6 \* I i9 F# I: P- i
$model->query('select * from user where id=%d and status=%s',$id,$status);
- }0 m( r3 U" T9 \* ~) A/ m7 W7 @; ]* s+ z+ G
或者+ v5 H" V. u" J+ v1 d+ j P0 I
$model->query('select * from user where id=%d and status=%s',array($id,$status));
8 e$ y) M/ D J
* F1 @& k# X; E) z! c0 g3 R9 g 原因:
2 V$ r, J; a7 ^, {* e# @ThinkPHP/Lib/Core/Model.class.php 文件里的parseSql函数没有实现SQL过滤.+ C& r4 x' E. j8 K3 {
原函数:# i1 x8 `. \ v. ^1 B+ }9 M( D
protected function parseSql($sql,$parse) {
7 Y8 i" S ]7 V2 W- Y1 q/ a // 分析表达式
( T+ S' m3 t& e& R, _; p3 g+ C if(true === $parse) {( v- h8 u. `) Y9 b+ ?8 C
$options = $this->_parseOptions();
1 N. I3 q h0 ?2 l$ q" l $sql = $this->db->parseSql($sql,$options);* @- |: E% `5 v7 H- o
}elseif(is_array($parse)){ // SQL预处理
3 _( @6 m( Y7 [) M9 Q/ k $sql = vsprintf($sql,$parse);
) g! b/ e4 C3 `1 w3 b }else{
- D) }$ @$ q, n! l# [ $sql = strtr($sql,array('__TABLE__'=>$this->getTableName(),'__PREFIX__'=>C('DB_PREFIX')));
. \ a$ q" s6 L! O6 k+ _. ~4 f }
6 P( p8 P3 R7 M, t9 T $this->db->setModel($this->name);' k$ l: \/ ?3 o- R
return $sql;
" w* d8 d/ }1 g/ r& z5 A" i$ ~; ~4 X }& z8 G! ]& q( O( c, H( q4 d8 L
0 D6 m3 j, U% `6 \3 b6 h
验证漏洞(举例):
$ s( Z5 T! L& D& w2 s% v请求地址:
2 V, s3 N8 k/ d) S- U- a9 {http://localhost/Main?id=boo” or 1=”11 M7 s# b0 a6 v! E9 }2 I; o
或+ ^9 [2 S/ o. {3 [# A. _& K7 J
http://localhost/Main?id=boo%22%20or%201=%2218 @5 K& `- X# G7 Z# a
action代码:4 @5 ?* \4 a$ s7 F
$model=M('Peipeidui');4 X- d: d; j1 K, I5 M4 }
$m=$model->query('select * from peipeidui where name="%s"',$_GET['id']);
' C8 m/ I7 O E% s3 \9 a: F dump($m);exit;, ]4 }' p% T# @ Y7 E% |+ u; [- Z
或者0 K4 G ]& L+ t% v$ |: @: @4 W# J2 q
$model=M('Peipeidui');" L6 \2 ~) v M, g* |
$m=$model->query('select * from peipeidui where name="%s"',array($_GET['id']));
/ H! v% m- l& h c" |8 n dump($m);exit;4 u* t- I" t8 O1 l8 Q: Z, w% r
结果:
5 {; A ?: l# g- d ?9 Y% D表peipeidui所有数据被列出,SQL注入语句起效.
! Z5 r" V+ z' H8 d5 G" G& h2 U解决办法:
Z0 U% q9 o3 U将parseSql函数修改为:
# m2 R2 r& w6 y( C& L9 `- jprotected function parseSql($sql,$parse) {4 ~, M4 K* s6 P* |- U2 r+ s- Q* o
// 分析表达式
) z( q7 v( @5 t6 d7 ^: Y6 W if(true === $parse) {
' E2 B& {" ?6 I1 p $options = $this->_parseOptions();
1 x# J0 v0 v1 C $sql = $this->db->parseSql($sql,$options);' @, m/ I8 N. \/ c6 H/ n; \
}elseif(is_array($parse)){ // SQL预处理
/ G! B* d* k0 X( |. z6 }8 Z8 X- J $parse = array_map(array($this->db,'escapeString'),$parse);//此行为新增代码 o/ h3 l* h9 ?, W: k$ g i9 y" V
$sql = vsprintf($sql,$parse);
! A( @- H$ t3 I% ^3 v }else{% L2 \9 S2 l4 o8 W! k1 K
$sql = strtr($sql,array('__TABLE__'=>$this->getTableName(),'__PREFIX__'=>C('DB_PREFIX')));
+ R$ K) n4 V9 z( q }
3 Y2 v# |+ o7 r; P, ]+ r' J7 ~ $this->db->setModel($this->name);
4 p1 C2 q# `7 q. K9 r' l return $sql;
! Y8 [: \( F, u: Y- ?* v }
% x! i: `' C! c- ^" [ H7 E. U7 | W
总结:& u! a* A. B, N# h0 w
不要过分依赖TP的底层SQL过滤,程序员要做好安全检查
* Z) l% S$ L! x$ b不建议直接用$_GET,$_POST3 {! o5 o6 B) \" y4 f: ]1 a3 a
[/td][/tr]
/ |. E" N2 G+ G- G. S2 U. M8 H[/table]+1% a1 N, s0 T& B& ^- f8 @
9 U) V: L8 X4 @) l1 l! f
: M! L" Y) h# J( q; D/ _ H |