标题: PHP程序的常见漏洞攻击分析 [打印本页] 作者: admin 时间: 2012-9-15 14:28 标题: PHP程序的常见漏洞攻击分析 PHP程序的常见漏洞攻击分析 ( ~' h2 y% U% x# a6 h; g2 w + R3 J4 z8 m% k" S& `" r & g# m" S5 w% f8 q4 N- I1 Z9 E转自:WhyTT5 Z; ~( D# k! m/ ]: z6 f
不是固若金汤,随着PHP的广泛运用,一些黑客们也在无时不想找PHP的麻烦,通过PHP程序漏洞进行攻击就是其中一种。在节,我们将从全局变量,远程文件,文件上载,库文件,Session文件,数据类型和容易出错的函数这几个方面分析了PHP的安全性。 ; ~7 g1 Q) S( H$ |$ M* u* c$ m. p) N% o5 V U
如何通过全局变量进行攻击?, C. r9 J3 x+ f8 W
1 ^: j4 b& V- J. DPHP中的变量不需要事先声明,它们会在第一次使用时自动创建,它们的类型根据上下文环境自动确定。从程序员的角度来看,这无疑是一种极其方便的处理方法。一旦一个变量被创建了,就可以在程序中的任何地方使用。这个特点导致的结果就是程序员很少初始化变量。+ {- w. Q6 v! F: k7 B& C
& l: k, I0 d2 t 1. In main.php: ' n3 r" R. O" }* V3 l4 D 2. <?php ; X( J1 L$ A* N& @/ H3 l 3. $libDir = "/libdir"; - I! n; @9 G( E- J+ E% h/ B
4. $langDir = "$libdir/languages"; ! a; Q5 s. \" D: _8 R$ v( c) g 5. ... # E0 S! n- E; m& d/ }8 H# Z5 @
6. include("$libdir/loadlanguage.php": ! s1 L6 U+ x X$ }
7. ?> 3 h# }; a1 ~- F, [
8. 3 R! ~' Y4 p/ O
9. In libdir/loadlanguage.php: " S; y) Q( c* w# f- \( W
10. <?php 9 C& ]1 k/ Z2 O: M 11. ... 6 @% }" o+ n1 `; h2 c% N" R2 ^; n
12. 8 F- N S1 [$ V7 X 13. include("$langDir/$userLang"); " ]3 V# _$ {2 q( j) @ |
14. ?> 3 _( \7 z7 f2 A% b- Q+ a% H5 w) R6 m; P. L
当"libdir/loadlanguage.php"被"main.php"调用时是相当安全的,但是因为"libdir /loadlanguage"具有".php"的扩展名,因此远程攻击者可以直接请求这个文件,并且可以任意指定"$langDir" 和"$userLang"的值。8 w2 r: O' P0 y4 f
/ G; J9 y: c5 ]2 x' o
如何通过Session文件进行攻击?' o# M' Y" d3 k& t1 I! ]9 m
/ A8 O1 [4 @6 d$ c3 Q
PHP 4或更新的版本提供了对sessions的支持,它的主要作用是在PHP程序中保存页与页之间的状态信息。例如,当一个用户登陆进入网站,他登陆了的这个事实以及谁登陆进入这个网站的相关信息都将被保存在session中,当他在网站中到处浏览时,所有的PHP代码都可以获得这些状态信息。2 `/ B# c8 A- C7 `. ~
5 }9 G7 {, b1 j2 V" t( [事实上,当一个session启动时(实际上是在配置文件中设置为在第一次请求时自动启动),就会生成一个随机的"session id",如果远程浏览器总是在发送请求时提交这个"session id"的话,session就会一直保持。这通过Cookie很容易实现,也可以通过在每页提交一个表单变量(包含"session id")来实现。PHP程序可以用session注册一个特殊的变量,它的值会在每个PHP脚本结束后存在session文件中,也会在每个PHP脚本开始前加载到变量中。下面是一个简单的例子:+ c F" Q; I" W
PHP代码 % {+ v; a8 c; A: Y" A$ i9 G, e9 a1 R" I" T
1. <?php 2 M! k: I5 }/ ^- b 2. session_destroy(); // Kill any data currently in the session * ?! X) ^7 k3 @0 G 3. $session_auth = "shaun"; , h/ }1 @: O+ a2 `0 v+ x! @- \ 4. session_register("session_auth"); // Register $session_auth as a session variable * N, s4 S: q/ E3 ~. A
5. ?> 9 v U) \+ O6 M Z+ G7 b
) E4 j. s. |$ S0 @新版本的PHP都会自动把"$session_auth"的值设置为"shaun",如果它们被修改的话,以后的脚本都会自动接受修改后的值,这对无状态的Web来说的确是种很不错的工具,但是我们也应该小心。' E# k6 i* X% Y* }
5 x# I+ w6 g' o5 c9 Z0 w一个很明显的问题就是确保变量的确来自session,例如,给定上面的代码,如果后续的脚本是下面这样的话:: O4 L8 k2 m H. P
PHP代码 # ]( Z; @; B0 e 6 v9 t& }* p+ Q7 r0 D 1. <?php 7 V2 A5 ^1 S4 B1 b2 L 2. if (!emptyempty($session_auth)) 6 q6 \0 L+ l& x8 D2 n% x- @
3. // Grant access to site here ; i- e! f) m. r8 v( ^ 4. ?> $ P: e2 y4 ~1 ?& P9 q z' S
' D9 g) H; r$ I( {& M7 _ 上面的代码假定如果"$session_auth"被赋值的话,就是从session,而不是从用户输入来赋值的,如果攻击者通过表单输入来赋值的话,他就可以获得对站点的访问权。注意攻击者必须在session注册该变量之前使用这种攻击方法,一旦变量被放进了session,就会覆盖任何表单输入。 ' e s+ O+ q M# @8 y1 O9 r 5 F: b* X/ Z# Z) ?Session数据一般是保存在文件中(位置是可配置的,一般是"/tmp"),文件名一般是类似"sess_<session id>"的形式,这个文件包含变量名称,变量类型,变量值和一些其它的数据。在多主机系统中,因为文件是以运行Web服务器的用户身份(一般是 nobody)保存的,因此恶意的站点拥有者就可以通过创建一个session文件来获得对其它站点的访问,甚至可以检查session文件中的敏感信息。, K9 h7 t* b, M r- k
+ A' Q; m* Q/ B: n- O' M. p& GSession机制也为攻击者把自己的输入保存在远程系统的文件中提供了另一个方便。对于上面的例子来说,攻击者需要在远程系统放置一个包含PHP代码的文件,如果不能利用文件上载做到的话,他通常会利用session为一个变量按照自己的意愿赋一个值,然后猜测session文件的位置,而他知道文件名是"php<session id>",所以只需猜测目录,而目录一般就是"/tmp"。; D: i& |# m X4 \6 _( z" X1 w
{6 P0 h% p4 A另外,攻击者可以任意指定"session id"(例如"hello"),然后用这个"session id"创建一个session文件(例如"/tmp/sess_hello"),但是"session id"只能是字母和数字组合。 0 I# ?" Q) I) R6 m8 {4 R. N% u+ b- f# V! y
如何通过数据类型进行攻击?/ @1 Q" m8 `, ^6 ]6 I% V& v
0 g. N3 ]0 X( n# u+ I; v3 i
PHP 具有比较松散的数据类型,变量的类型依赖于它们所处的上下文环境。例如:"$hello"开始是字符串变量,值为"",但是在求值时,就变成了整形变量"0",这有时可能会导致一些意想不到的结果。如果"$hello"的值为"000"还是为"0"是不同的,empty()返回的结果也不会为真。7 I- l" ~6 o1 D+ n6 g T
+ a/ R' r. Z4 F7 V( [/ D
PHP中的数组是关联数组,也就是说,数组的索引是字符串型的。这意味着"$hello["000"]"和"$hello[0]"也是不同的。. u' U7 v+ _& b* z* g3 S
/ R+ A p" d8 X& i: N2 h! L
开发程序的时候应该仔细地考虑上面的问题,例如,我们不应该在一个地方测试某个变量是否为"0",而在另外的地方使用empty()来验证。 0 U/ H0 l$ {7 h$ V% a s$ L* @- E: D% L3 r& I
如何通过容易出错的函数进行攻击?下面是一份比较详细的容易出错的函数列表: 2 l* |' F: ^6 u7 N2 {9 A9 ~# T6 FPHP代码 0 s4 h9 Y4 T; T5 Q& i; L8 x, S5 u# L' q5 }" ]
1. <PHP代码执行> % ]& ^! X$ N: e9 P$ s* b 2. require():读取指定文件的内容并且作为PHP代码解释 & q2 A" i2 \; ]% l1 D+ P/ g 3. include():同上 + {( K- A. N" j; \0 ~3 x1 @ 4. eval():把给定的字符串作为PHP代码执行 ! U; C J9 i7 l8 m% P 5. preg_replace():当与"/e"开关一起使用时,替换字符串将被解释为PHP代码 " N! x* x3 Q0 b/ C3 r9 P1 X f 6. . E1 h1 |0 U( w ] 7. <命令执行> . p7 l2 N; A8 u- z* S
8. exec():执行指定的命令,返回执行结果的最后一行 . X% _* B: `/ \8 Q 9. passthru():执行指定命令,返回所有结果到客户浏览器 + L) x* b* u) |" R; t5 S, r8 e2 Y 10. ``:执行指定命令,返回所有结果到一个数组 . |6 ]2 a% n: B4 ]( |" ^6 e. e 11. system():同passthru(),但是不处理二进制数据 2 m. x) Z0 Q+ |" i4 X8 |# a
12. popen():执行指定的命令,把输入或输出连接到PHP文件描述符 ' Y6 s' D5 [* L4 J 13. & t+ a2 b7 n0 l9 q 14. <文件泄露> : k# G5 j, e: i! \: \( w
15. fopen():打开文件,并对应一个PHP文件描述符 ) u2 u/ U2 _9 z4 Q8 }
16. readfile():读取文件的内容,然后输出到客户浏览器 ' Z8 i7 q! T/ m8 _ l3 ?9 { 17. file():把整个文件内容读到一个数组中 4 Z6 q" f- e: c4 r& `/ b' l ) L; H$ s, i! W& Q0 J6 U 如何增强PHP的安全性? : J3 s, F5 v/ R+ {) E 4 }) X7 R6 ]6 v/ j+ A我们在上面介绍的所有攻击对于缺省安装的PHP4都可以很好的实现,但是PHP的配置非常灵活,通过配置一些PHP选项,我们完全可能抵抗其中的一些攻击。下面我们按照实现的难度对一些配置进行了分类:+ [/ j! B6 r8 b7 Z
& q$ e, T K. r1 V: ]*低难度 ( E+ c4 V- l0 ?- q" ^3 T& O. m**中低难度 e' J! v( V+ Z$ j# ~***中高难度3 [# U/ [2 |+ r4 L! O. c* F
****高难度: S L3 y9 m7 d: I' {2 d
: L8 a6 G2 `+ m ~/ o- g
如果你使用了PHP提供的所有选项的话,那么你的PHP将是很安全的,即使是第三方的代码也是如此,因为其中很多功能已经不能使用。& ]3 g* ]. T' i; m/ c0 t
! ]! F9 y# Y- W1 J, q
**** 设置"register_globals"为"off"5 |7 \- Z. A2 J0 m. p" G5 }
这个选项会禁止PHP为用户输入创建全局变量,也就是说,如果用户提交表单变量"hello",PHP不会创建"$ hello",而只会创建"HTTP_GET/POST_VARS['hello']"。这是PHP中一个极其重要的选项,关闭这个选项,会给编程带来很大的不便。) l/ y2 H* M5 D, P
. k- r$ ^0 t# u5 @. @
*** 设置"safe_mode"为"on"+ z$ L# _/ Y- D5 F
4 N+ z9 E8 O: V7 B9 z
打开这个选项,会增加如下限制: 4 V! G/ j+ @- S% ~* O6 o: c7 h. M6 a' E' Q$ k& b
1. 限制哪个命令可以被执行 3 b! L9 Q" A" b: Z9 m2. 限制哪个函数可以被使用8 c6 y# m$ c, z* a
3. 基于脚本所有权和目标文件所有权的文件访问限制 ' Q, F# y* H( Z" {& t( } [5 E4. 禁止文件上载功能 + `$ O# {8 g8 E! R. j- s+ \- o % d _. S1 j4 W: F U这对于ISP来说是一个"伟大"的选项,同时它也能极大地改进PHP的安全性。 $ z' @+ v) M$ B0 e : V8 o: E. W- z- R) \% y** 设置"open_basedir" ; l' `6 E2 y' h- l" y* ^, f$ h* A% G2 o6 U, z% c1 N- H
这个选项可以禁止指定目录之外的文件操作,有效地消除了本地文件或者是远程文件被include()的攻击,但是仍需要注意文件上载和session文件的攻击。 & }" x6 M f& P5 a- H ) N, E0 ^' [* o/ \9 J2 q** 设置"display_errors"为"off",设置"log_errors"为"on"- _9 g- ?" {5 K; P& j
1 }7 p% V2 S( t8 a, @% r
这个选项禁止把错误信息显示在网页中,而是记录到日志文件中,这可以有效的抵制攻击者对目标脚本中函数的探测。 ) } F2 V! [% t6 T" G+ ]; L) t7 H2 H
* 设置"allow_url_fopen"为"off" * B) [1 g0 Z, P5 t$ @4 P 9 J! [7 f& ?. q5 ^7 b这个选项可以禁止远程文件功能。- O0 S: P( H8 P- _3 [2 h. s% F+ P
5 i) h& y% x; k( P* {
4 n6 e& @0 }+ g/ Y* o# o- w% d( F9 J& a4 M5 z
. g( H& u1 f O7 C: C# p
//这里allow_url_fopen 注意下,在jnc blog上看到,可以用3 C0 I' Z, \+ R! a$ Z
PHP代码- y3 m( G0 Q' ` Q/ N' }2 A; ] K3 O
2 L+ c" b) W' \7 ?
1. <?php ) j. B+ X9 u) B2 p. F' y. _3 h
2. include('\\myip\test.php'); E) R5 P7 N/ u8 A9 j 3. ?> # t, H' `) i: x* t2 c& S8 Y 4. ; S4 e6 @( P1 R# {5 j: _7 ~3 c
5. <?php 3 J2 z. N4 i; W3 [; [+ h3 I. v 6. include('//myip\test.php'); + T6 \. K' y: L( W 7. ?> p8 V" w! Z! C; G* q. O$ M. k* `7 @- Q9 z3 T
来饶过?