跨站请求伪造CSRF
CSRF是Cross Site Request Forgery的缩写,乍一看和XSS差不多的样子,但是其原理正好相反,XSS是利用合法用户获取其信息,而CSRF是伪造成合法用户发起请求。
在XSS危害——session劫持中我们提到了session原理,用户登录后会把登录信息存放在服务器,客户端有一个用户标识存在cookie中,只要用户不关闭浏览器或者退出登录,在其有效期内服务器就会把这个浏览器发送的请求当作当前客户,如果这时候用户被欺骗,使用浏览器打开了某些恶意网址,里面就会包含一些不是用户希望发送的请求,服务器也会把这些请求当作是当前客户发送的请求,这时候用户的个人信息、资金安全、如果用户权限高整个站点都可能会受到危害。
CSRF原理
CSRF原理很简单,当用户登录以站点时用浏览器打开一恶意网址,就有可能遭受攻击。有同学会奇怪了这个很难实现吧,必须同时满足两个条件才行。其实很简单,比如我们使用QQ,看看QQ zone,突然蹦出个包含中奖或者问卷调查链接的聊天窗口(或者是。。。),这个腾讯做了防范,但是我们收到封邮件包含此内容,很多用户会选择去点击
简单小例子
在某个论坛管理页面,管理员可以在list.php页面执行删除帖子操作,根据URL判断删除帖子的id,像这样的一个URL
http://localhost/list.php?action=delete&id=12
当恶意用户想管理员发送包含CSFR的邮件,骗取管理员访问http://test.com/csrf.php
,在这个恶意网页中只要包含这样的html语句就可以利用让管理员在不知情的情况下删除帖子了
<img alt="" arc="http://localhost/list.php?action=delete&id=12"/>
这个利用了img的src可以跨域请求的特点,这种情况比较少,因为一般网站不会利用get请求修改资源信息
升级
是不是网站利用post修改信息就安全了呢,还拿刚才例子,改成post修改的
<?php
$action=$_POST['action'];
$id=$_POST['id'];
delete($action,$id);
?>
但是恶意网站这么写一样可以攻击
<!DOCTYPE html>
<html>
<body>
<iframe display="none">
<form method="post" action="http://localhost/list.php">
<input type="hidden" name="action" value="delete">
<input type="hidden" name="id" value="12">
<input id="csfr" type="submit"/>
</form>
</iframe>
<script type="text/javascript">
document.getElementById('csfr').submit();
</script>
</body>
</html>
攻击演示
如下表单
<form action="buy.php" method="POST">
<p>
Item:
<select name="item">
<option name="pen">pen</option>
<option name="pencil">pencil</option>
</select><br />
Quantity: <input type="text" name="quantity" /><br />
<input type="submit" value="Buy" />
</p>
</form>
php请求处理
<?php
session_start();
$clean = array();
if (isset($_REQUEST['item'] && isset($_REQUEST['quantity']))
{
/* Filter Input ($_REQUEST['item'], $_REQUEST['quantity']) */
if (buy_item($clean['item'], $clean['quantity']))
{
echo '<p>Thanks for your purchase.</p>';
}
else
{
echo '<p>There was a problem with your order.</p>';
}
}
?>
攻击者尝试使用get方式:http://store.example.org/buy.php?item=pen&quantity=1
如果能成功的话,攻击者如果取得了当合法用户访问时,可以引发购买的URL格式。在这种情况下,进行跨站请求伪造攻击非常容易,因为攻击者只要引发受害者访问该URL即可
攻击者并不需要取得用户授权
用户再访问其他网站的时候,如果这个网站引导用户发起了上面的请求,浏览器会带上用户相关的cookie,相当于非法网站取得了用户授权。
虽然有多种发起跨站请求伪造攻击的方式,但是使用嵌入资源如图片的方式是最普遍的。为了理解这个攻击的过程,首先有必要了解浏览器请求这些资源的方式。
当你访问http://www.google.com
,你的浏览器首先会请求这个URL所标识的资源。你可以通过查看该页的源文件(HTML)的方式来看到该请求的返回内容。在浏览器解析了返回内容后发现了Google的标志图片。这个图片是以HTML的img标签表示的,该标签的src属性表示了图片的URL。浏览器于是再发出对该图片的请求,以上这两次请求间的不同点只是URL的不同。
根据上面的原理,跨站请求伪造攻击可以通过img标签来实现。考虑一下如果访问包括下面的源代码的网页会发生什么情况:
<img src="http://store.example.org/buy.php?item=pencil&quantity=50" />
由于buy.php脚本使用 R E Q U E S T 而 不 是 _REQUEST而不是 REQUEST而不是_POST,这样每一个只要是登录在store.example.org商店上的用户就会通过请求该URL购买50支铅笔。
跨站请求伪造攻击的存在是不推荐使用$_REQUEST的原因之一。
防范方法
-
使用post,不使用get修改信息
-
验证码,所有表单的提交需要验证码,但是貌似用起来很麻烦,所以一些关键的操作可以
-
在表单中预先植入一些加密信息,验证请求是此表单发送
使用POST方式而不是使用GET来提交表单,在处理表单提交时使用$_POST
而不是$_REQUEST
验证表单来自真正的页面,而不是伪造的。需要验证。
生成表单token
<?php
session_start();
$token = md5(uniqid(rand(), TRUE));
$_SESSION['token'] = $token;
$_SESSION['token_time'] = time();
?>
提交表单的时候,token也以前提价到服务器。
<form action="buy.php" method="POST">
<input type="hidden" name="token" value="<?php echo $token; ?>" />
<p>
Item:
<select name="item">
<option name="pen">pen</option>
<option name="pencil">pencil</option>
</select><br />
Quantity: <input type="text" name="quantity" /><br />
<input type="submit" value="Buy" />
</p>
</form>
一个跨站请求伪造攻击就必须包括一个合法的验证码以完全模仿表单提交。由于验证码的保存在用户的session中的,攻击者必须对每个受害者使用不同的验证码。这样就有效的限制了对一个用户的任何攻击,它要求攻击者获取另外一个用户的合法验证码。使用你自己的验证码来伪造另外一个用户的请求是无效的。
该验证码可以简单地通过一个条件表达式来进行检查:
<?php
if (isset($_SESSION['token']) && $_POST['token'] == $_SESSION['token'])
{
/* Valid Token */
}
?>
你还能对验证码加上一个有效时间限制,如5分钟,并且让token只能使用一次:
<?php
$token_age = time() - $_SESSION['token_time'];
$token = $_SESSION['token'];
unset($_SESSION['token']);
if ($token_age <= 300)
{
/* Less than five minutes has passed. */
}
?>
通过在你的表单中包括验证码,你事实上已经消除了跨站请求伪造攻击的风险。可以在任何需要执行操作的任何表单中使用这个流程。
你也可以让token只能使用一次,在每次请求被请求后token通过后销毁这个token。可以更安全,同时也可以防止表单重复提交。
防范方法
使用cookei验证
如果我们不考虑用户的Cookies很容易由于网站中存在XSS漏洞而被偷窃(我们已经知道这样的事情并不少见)这一事实,这是一个很好的应对对CSRF的解决方案。如果我们为用户的每一个表单请求中都加入随机的Cookies,那么这种方法会变得更加安全,但是这并不是十分合适
<?php
// Hash the cookie
$hash = md5($_COOKIE['cookie']);
?>
<form method="POST" action="resolve.php">
<input type="text" name="first_name">
<input type="text" name="last_name">
<input type="hidden" name="check" value="<?=$hash;?>">
<input type="submit" name="submit" value="Submit">
</form>
<?php
// Check if the "check" var exists
if(isset($_POST['check'])) {
$hash = md5($_COOKIE['cookie']);
// Check if the values coincide
if($_POST['check'] == $hash) {
do_something();
} else {
echo "Malicious Request!"$$
}
} else {
echo "Malicious Request!"$$
}
?>
这个方案的思路是:每次的用户提交都需要用户在表单中填写一个图片上的随机字符串,厄…这个方案可以完全解决CSRF,但个人觉得在易用性方面似乎不是太好
最后,为了感谢读者们,我想把我收藏的一些网络安全/渗透测试学习干货贡献给大家,回馈每一个读者,希望能帮到你们。
干货主要有: