跨站请求伪造 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 脚本使用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,但个人觉得在易用性方面似乎不是太好
最后,为了感谢读者们,我想把我收藏的一些网络安全/渗透测试学习干货贡献给大家,回馈每一个读者,希望能帮到你们。
干货主要有:
【资料领取】
评论