csrf原理:
我对csrf的理解为一种安全验证。CSRF跨站点请求伪造(Cross—Site Request Forgery),跟XSS攻击一样,存在巨大的危害性,你可以这样来理解:攻击者盗用了你的身份,以你的名义发送恶意请求,对服务器来说这个请求是完全合法的,就是黑客运用正常用户的身份进行操作。但是却完成了攻击者所期望的一个操作,比如以你的名义发送邮件、发消息,盗取你的账号,添加系统管理员,甚至于购买商品、虚拟货币转账等。 如下:其中Web A为存在CSRF漏洞的网站,Web B为攻击者构建的恶意网站,User C为Web A网站的合法用户。
CSRF攻击攻击原理及过程如下:
1. 用户C打开浏览器,c为合法用户。访问受信任网站A,输入用户名和密码请求登录网站A;
2.在用户信息通过验证后,网站A产生Cookie信息并返回给浏览器,此时用户登录网站A成功,可以正常发送
请求到网站A;进行用户c进行的合法操作。此时用户的cookie信息已经在A浏览器
3. 用户未退出网站A之前,在同一浏览器中,打开一个TAB页访问网站B;ps:b为攻击者构造的恶意网站
4. 网站B接收到用户请求后,返回一些攻击性代码,并发出一个请求要求访问第三方站点A;
5. 浏览器在接收到这些攻击性代码后,根据网站B的请求,在用户不知情的情况下携带Cookie信息,网站b攻
击者向网站A发出请求。网站A并不知道该请求其实是由B发起的,所以会根据用户C的Cookie信息以C的权限处理
该请求,导致来自网站B的恶意代码被执行。
6,使攻击者达到期望达到的目的。
现在我们来模拟一下curf的恶意攻击:这是一个不是很安全的网站,模拟了转账的系统,可以正常运行。
<?php
//pdo连接数据库方法
$dsn="mysql:dbname=stucourse;host=127.0.0.1";
//数据库的用户名
$user="root";
//数据库的密码
$password="root";
$object = new PDO($dsn,$user,$password);
$hidden=empty($_POST['hidden'])?'':$_POST['hidden'];
if(!$hidden) {
$sql = "select * from student";
$object->setAttribute(PDO::ATTR_ERRMODE, PDO::CASE_UPPER);
$result = $object->query($sql);
$result->setFetchMode(PDO::FETCH_ASSOC);
$result_array = $result->fetchAll();
}else{
#执行转账
try{
//生成PDO对象(面向对象);
$object = new PDO($dsn,$user,$password);
$object->beginTransaction(); // 开启一个事务
$sql="update student set stu_balance=stu_balance-10 WHERE stu_name='小王'";
$sql2="update student set stu_balance=stu_balance+10 WHERE stu_name='小张'";
//PDO->exec() — 处理一条SQL语句,并返回所影响的条目数
$result = $object->exec($sql);
$result2 = $object->exec($sql2);
if(($result+$result2)==2){
$object->commit();//执行成功-》提交事务
echo "转账成功!";
header( "Location: pdo_rollback.php" );
}else{
$object->rollback(); // 执行失败,事务回滚
throw new PDOException('操作失败');
}
}catch(PDOException $e){
die ("Error!: " . $e->getMessage() . "<br/>");
}
}
?>
<h1>转账功能</h1>
<form action="pdo_rollback.php" method="post">
<input type="hidden" value="1" name="hidden">
<table border="1">
<tr>
<td>姓名</td>
<td>剩余金额</td>
</tr>
<?php foreach($result_array as $k=>$v){ ?>
<tr>
<td><?php echo $v['stu_name'] ?></td>
<td><?php echo $v['stu_balance'] ?></td>
</tr>
<?php } ?>
<tr>
<td colspan="3"><input type="submit" value="执行(金额为10¥)" class="submit"></td>
</tr>
</table>
</form>
在浏览器中展示出来的效果是这样的:
正常用户运行,小王将给小张转账10元钱,接下来我已一个黑客的角色再模拟一个这样的网站,右击查看网页原代码:
<h1>转账功能</h1> | |
<form action="pdo_rollback.php" method="post"> | |
<input type="hidden" value="1" name="hidden"> | |
<table border="1"> | |
<tr> | |
<td>姓名</td> | |
<td>剩余金额</td> | |
</tr> | |
<tr> | |
<td>小王</td> | |
<td>100</td> | |
</tr> | |
<tr> | |
<td>小张</td> | |
<td>1240</td> | |
</tr> | |
<tr> | |
<td colspan="3"><input type="submit" value="执行(金额为10¥)" class="submit"></td> | |
</tr> | |
</table> | |
</form> | |
然后我们需要加一个js脚本,发送请求到转账系统,以下是一个攻击的代码:
<script src="../month6/thinkphp_3.2.3_full/Public/Admin/js/jquery-3.2.0.min.js"></script>
<h1>转账功能</h1>
<!--<form action="pdo_rollback.php" method="post">-->
<input type="hidden" value="1" name="hidden">
<table border="1">
<tr>
<td>姓名</td>
<td>剩余金额</td>
</tr>
<tr>
<td>小王</td>
<td>-1120</td>
</tr>
<tr>
<td>小张</td>
<td>1020</td>
</tr>
<tr>
<td colspan="3"><input type="submit" value="执行(金额为10¥)" class="submit"></td>
</tr>
</table>
<!--</form>-->
<script>
</script>
<script>
$('.submit').on('click',function(){
$.post({
url:'http://localhost/month7/pdo_rollback.php',
type:'post',
data:{hidden:1},
success:function(json_data){
console.log(json_data);
}
})
})
</script>
每次刷新,小王都讲自动给小张转账10元,这个转账系统遭受了恶意攻击。
那么,如何防止curf的攻击呢?
1,添加一个token请求
在这里我做了一个随机生成一个token,将token存入session(这里也可以存入数据库)并将token作为表单提交post传来的值,然后通过请求转账的时候通过token验证用户身份,判断是否为合法请求,具体改进代码如下:
<?php
session_start();//先匹配session
//pdo连接数据库方法
$dsn="mysql:dbname=stucourse;host=127.0.0.1";
//数据库的用户名
$user="root";
//数据库的密码
$password="root";
$object = new PDO($dsn,$user,$password);
$hidden=empty($_POST['hidden'])?'':$_POST['hidden'];
if(!$hidden) {
$csrf_token=rand(10000,99999);
$new_token=md5('pdo_rollback.php'.$csrf_token);
$_SESSION['csrf_token']=$new_token;
// print_r($_SESSION['csrf_token']);exit;
$sql = "select * from student";
$object->setAttribute(PDO::ATTR_ERRMODE, PDO::CASE_UPPER);
$result = $object->query($sql);
$result->setFetchMode(PDO::FETCH_ASSOC);
$result_array = $result->fetchAll();
}else{
#执行转账
if($hidden!=$_SESSION['csrf_token']){
echo '非法请求1!';exit;
}else{
try{
//生成PDO对象(面向对象);
$object = new PDO($dsn,$user,$password);
$object->beginTransaction(); // 开启一个事务
$sql="update student set stu_balance=stu_balance-10 WHERE stu_name='小王'";
$sql2="update student set stu_balance=stu_balance+10 WHERE stu_name='小张'";
//PDO->exec() — 处理一条SQL语句,并返回所影响的条目数
$result = $object->exec($sql);
$result2 = $object->exec($sql2);
if(($result+$result2)==2){
$object->commit();//执行成功-》提交事务
echo "转账成功!";
header( "Location: pdo_rollback.php" );
}else{
$object->rollback(); // 执行失败,事务回滚
throw new PDOException('操作失败');
}
}catch(PDOException $e){
die ("Error!: " . $e->getMessage() . "<br/>");
}
}
}
?>
<h1>转账功能</h1>
<form action="pdo_rollback.php" method="post">
<input type="text" value="<?=$new_token ?>" name="hidden">
<table border="1">
<tr>
<td>姓名</td>
<td>剩余金额</td>
</tr>
<?php foreach($result_array as $k=>$v){ ?>
<tr>
<td><?php echo $v['stu_name'] ?></td>
<td><?php echo $v['stu_balance'] ?></td>
</tr>
<?php } ?>
<tr>
<td colspan="3"><input type="submit" value="执行(金额为10¥)" class="submit"></td>
</tr>
</table>
</form>
红色标记的为改进好的!
对于防止csrf攻击还有以下几种解决措施:
1. 尽量使用POST,限制GET
GET接口太容易被拿来做CSRF攻击,看第一个示例就知道,只要构造一个img标签,而img标签又是不能过滤的数据。接口最好限制为POST使用,GET则无效,降低攻击风险。
当然POST并不是万无一失,攻击者只要构造一个form就可以,但需要在第三方页面做,这样就增加暴露的可能性。
2. 浏览器Cookie策略
IE6、7、8、Safari会默认拦截第三方本地Cookie(Third-party Cookie)的发送。但是Firefox2、3、Opera、Chrome、Android等不会拦截,所以通过浏览器Cookie策略来防御CSRF攻击不靠谱,只能说是降低了风险。
PS:Cookie分为两种,Session Cookie(在浏览器关闭后,就会失效,保存到内存里),Third-party Cookie(即只有到了Exprie时间后才会失效的Cookie,这种Cookie会保存到本地)。
PS:另外如果网站返回HTTP头包含P3P Header,那么将允许浏览器发送第三方Cookie。
3. 加验证码
验证码,强制用户必须与应用进行交互,才能完成最终请求。在通常情况下,验证码能很好遏制CSRF攻击。但是出于用户体验考虑,网站不能给所有的操作都加上验证码。因此验证码只能作为一种辅助手段,不能作为主要解决方案。
4. Referer Check
Referer Check在Web最常见的应用就是“防止图片盗链”。同理,Referer Check也可以被用于检查请求是否来自合法的“源”(Referer值是否是指定页面,或者网站的域),如果都不是,那么就极可能是CSRF攻击。
但是因为服务器并不是什么时候都能取到Referer,所以也无法作为CSRF防御的主要手段。但是用Referer Check来监控CSRF攻击的发生,倒是一种可行的方法。
5. Anti CSRF Token
现在业界对CSRF的防御,一致的做法是使用一个Token(Anti CSRF Token)。
例子:
1. 用户访问某个表单页面。
2. 服务端生成一个Token,放在用户的Session中,或者浏览器的Cookie中。
3. 在页面表单附带上Token参数。
4. 用户提交请求后, 服务端验证表单中的Token是否与用户Session(或Cookies)中的Token一致,一致为合法请求,不是则非法请求。
这个Token的值必须是随机的,不可预测的。由于Token的存在,攻击者无法再构造一个带有合法Token的请求实施CSRF攻击。另外使用Token时应注意Token的保密性,尽量把敏感操作由GET改为POST,以form或AJAX形式提交,避免Token泄露。
注意:
CSRF的Token仅仅用于对抗CSRF攻击。当网站同时存在XSS漏洞时候,那这个方案也是空谈。所以XSS带来的问题,应该使用XSS的防御方案予以解决。
总结
CSRF攻击是攻击者利用用户的身份操作用户帐户的一种攻击方式,通常使用Anti CSRF Token来防御CSRF攻击,同时要注意Token的保密性和随机性。
有什么不对的还请各位大神指出来!
转载自:https://www.cnblogs.com/lovesong/p/5233195.html