CSRF的全名为Cross-site request forgery,它的中文名为 跨站请求伪造(伪造跨站请求【这样读顺口一点】)javascript
CSRF是一种夹持用户在已经登录的web应用程序上执行非本意的操做的攻击方式。相比于XSS,CSRF是利用了系统对页面浏览器的信任,XSS则利用了系统对用户的信任。php
下面为CSRF攻击原理图:html
由上图分析咱们能够知道构成CSRF攻击是有条件的:java
一、客户端必须一个网站并生成cookie凭证存储在浏览器中web
二、该cookie没有清除,客户端又tab一个页面进行访问别的网站数组
咱们就以游戏虚拟币转帐为例子进行分析浏览器
假设某游戏网站的虚拟币转帐是采用GET方式进行操做的,样式如:安全
1 http://www.game.com/Transfer.php?toUserId=11&vMoney=1000
此时恶意攻击者的网站也构建一个类似的连接:服务器
一、能够是采用图片隐藏,页面一打开就自动进行访问第三方文章:<img src='攻击连接'>cookie
二、也能够采用js进行相应的操做
http://www.game.com/Transfer.php?toUserId=20&vMoney=1000 #toUserID为攻击的帐号ID
一、倘若客户端已经验证并登录www.game.com网站,此时客户端浏览器保存了游戏网站的验证cookie
二、客户端再tab另外一个页面进行访问恶意攻击者的网站,并从恶意攻击者的网站构造的连接来访问游戏网站
三、浏览器将会携带该游戏网站的cookie进行访问,刷一下就没了1000游戏虚拟币
游戏网站负责人认识到了有被攻击的漏洞,将进行升级改进。
将由连接GET提交数据改为了表单提交数据
//提交数据表单
<form action="./Transfer.php" method="POST"> <p>toUserId: <input type="text" name="toUserId" /</p> <p>vMoney: <input type="text" name="vMoney" /></p> <p><input type="submit" value="Transfer" /></p> </form>
Transfer.php
1 <?php 2 session_start(); 3 if (isset($_REQUEST['toUserId'] && isset($_REQUEST['vMoney'])) #验证 4 { 5 //相应的转帐操做 6 } 7 ?>
恶意攻击者将会观察网站的表单形式,并进行相应的测试。
首先恶意攻击者采用(http://www.game.com/Transfer.php?toUserId=20&vMoney=1000)进行测试,发现仍然能够转帐。
那么此时游戏网站所作的更改没起到任何的防范做用,恶意攻击者只须要像上面那样进行攻击便可达到目的。
总结:
一、网站开发者的错误点在于没有使用$_POST进行接收数据。当$_REQUEST能够接收POST和GET发来的数据,所以漏洞就产生了。
这一次,游戏网站开发者又再一次认识到了错误,将进行下一步的改进与升级,将采用POST来接收数据
Transfer.php
1 <?php 2 session_start(); 3 if (isset($_POST['toUserId'] && isset($_POST['vMoney'])) #验证 4 { 5 //相应的转帐操做 6 } 7 ?>
此时恶意攻击者就没有办法进行攻击了么?那是不可能的。
恶意攻击者根据游戏虚拟币转帐表单进行伪造了一份如出一辙的转帐表单,而且嵌入到iframe中
嵌套页面:(用户访问恶意攻击者主机的页面,即tab的新页面)
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>攻击者主机页面</title> <script type="text/javascript"> function csrf() { window.frames['steal'].document.forms[0].submit(); } </script> </head> <body onload="csrf()"> <iframe name="steal" display="none" src="./xsrf.html"> </iframe> </body> </html>
表单页面:(csrf.html)
<!DOCTYPE html> <html> <head> <title>csrf</title> </head> <body> <form display="none" action="http://www.game.com/Transfer.php" method="post" > <input type="hidden" name="toUserID" value="20"> <input type="hidden" name="vMoney" value="1000"> </form> </body> </html>
客户端访问恶意攻击者的页面同样会遭受攻击。
总结:
CSRF攻击是源于Web的隐式身份验证机制!Web的身份验证机制虽然能够保证一个请求是来自于某个用户的浏览器,但却没法保证该请求是用户批准发送的
服务器端防护:
一、重要数据交互采用POST进行接收,固然是用POST也不是万能的,伪造一个form表单便可破解
二、使用验证码,只要是涉及到数据交互就先进行验证码验证,这个方法能够彻底解决CSRF。可是出于用户体验考虑,网站不能给全部的操做都加上验证码。所以验证码只能做为一种辅助手段,不能做为主要解决方案。
三、验证HTTP Referer字段,该字段记录了这次HTTP请求的来源地址,最多见的应用是图片防盗链。PHP中能够采用APache URL重写规则进行防护,可参考:http://www.cnblogs.com/phpstudy2015-6/p/6715892.html
四、为每一个表单添加令牌token并验证
(可使用cookie或者session进行构造。固然这个token仅仅只是针对CSRF攻击,在这前提须要解决好XSS攻击,不然这里也将会是白忙一场【XSS能够偷取客户端的cookie】)
CSRF攻击之因此可以成功,是由于攻击者能够伪造用户的请求,该请求中全部的用户验证信息都存在于Cookie中,所以攻击者能够在不知道这些验证信息的状况下直接利用用户本身的Cookie来经过安全验证。由此可知,抵御CSRF攻击的关键在于:在请求中放入攻击者所不能伪造的信息,而且该信息不存在于Cookie之中。
鉴于此,咱们将为每个表单生成一个随机数秘钥,并在服务器端创建一个拦截器来验证这个token,若是请求中没有token或者token内容不正确,则认为多是CSRF攻击而拒绝该请求。
因为这个token是随机不可预测的而且是隐藏看不见的,所以恶意攻击者就不可以伪造这个表单进行CSRF攻击了。
要求:
一、要确保同一页面中每一个表单都含有本身惟一的令牌
二、验证后须要删除相应的随机数
构造令牌类Token.calss.php
1 <?php 2 class Token 3 { 4 /** 5 * @desc 获取随机数 6 * 7 * @return string 返回随机数字符串 8 */ 9 private function getTokenValue() 10 { 11 return md5(uniqid(rand(), true).time()); 12 } 13 14 /** 15 * @desc 获取秘钥 16 * 17 * @param $tokenName string | 与秘钥值配对成键值对存入session中(标识符,保证惟一性) 18 * 19 * @return array 返回存储在session中秘钥值 20 */ 21 public function getToken($tokenName) 22 { 23 $token['name']=$tokenName; #先将$tokenName放入数组中 24 session_start(); 25 if(@$_SESSION[$tokenName]) #判断该用户是否存储了该session 26 { #是,则直接返回已经存储的秘钥 27 $token['value']=$_SESSION[$tokenName]; 28 return $token; 29 } 30 else #否,则生成秘钥并保存 31 { 32 $token['value']=$this->getTokenValue(); 33 $_SESSION[$tokenName]=$token['value']; 34 return $token; 35 } 36 } 37 38 } 39 #测试 40 $csrf=new Token(); 41 $name='form1'; 42 $a=$csrf->getToken($name); 43 echo "<pre>"; 44 print_r($a); 45 echo "</pre>"; 46 echo "<pre>"; 47 print_r($_SESSION); 48 echo "</pre>";die; 49 50 ?>
表单中使用:
1 <?php 2 session_start(); 3 include(”Token.class.php”); 4 $token=new Token(); 5 $arr=$token->getToken(‘transfer’); #保证惟一性(标识符) 6 ?> 7 <form method=”POST” action=”./transfer.php”> 8 <input type=”text” name=”toUserId”> 9 <input type=”text” name=”vMoney”> 10 <input type="hidden" name="<?php echo $arr['name'] ?>" value="<?php echo $arr['value']?>" > 11 <input type=”submit” name=”submit” value=”Submit”> 12 </from>
验证:
1 <?php 2 #转帐表单验证 3 session_start(); 4 if($_POST['transfer']==$_SESSION['transfer']) #先检验秘钥 5 { 6 unset($_SESSION['transfer']); #删除已经检验的存储秘钥 7 if ( &&isset($_POST['toUserId'] && isset($_POST['vMoney'])) #验证 8 { 9 //相应的转帐操做 10 } 11 } 12 else 13 { 14 return false; 15 } 16 ?>
1 <?php 2 #转帐表单验证 3 session_start(); 4 if($_POST['transfer']==$_SESSION['transfer']) #先检验秘钥 5 { 6 unset($_SESSION['transfer']); #删除已经检验的存储秘钥 7 if ( &&isset($_POST['toUserId'] && isset($_POST['vMoney'])) #验证 8 { 9 //相应的转帐操做 10 } 11 } 12 else 13 { 14 return false; 15 } 16 ?>
该方法套路:
1. 用户访问某个表单页面。
2. 服务端生成一个Token,放在用户的Session中,或者浏览器的Cookie中。【这里已经不考虑XSS攻击】
3. 在页面表单附带上Token参数。
4. 用户提交请求后, 服务端验证表单中的Token是否与用户Session(或Cookies)中的Token一致,一致为合法请求,不是则非法请求。
1. 《浅谈CSRF攻击方式》
2. 《Web安全之CSRF攻击》
(以上是本身的一些看法,如有不足或者错误的地方请各位指出)
做者:那一叶随风 http://www.cnblogs.com/phpstudy2015-6/
原文地址:http://www.cnblogs.com/phpstudy2015-6/p/6771239.html
声明:本博客文章为原创,只表明本人在工做学习中某一时间内总结的观点或结论。转载时请在文章页面明显位置给出原文连接