CSRF(Cross-site request forgery跨站请求伪造,也被称成为“one click attack”或者session riding,通常缩写为CSRF或者XSRF,是一种对网站的恶意利用。
一、CSRF攻击原理
CSRF攻击原理比较简单,如图1所示。其中Web A为存在CSRF漏洞的网站,Web B为攻击者构建的恶意网站,User C为Web A网站的合法用户。
图1 CSRF攻击原理
1. 用户C打开浏览器,访问受信任网站A,输入用户名和密码请求登录网站A;
2.在用户信息通过验证后,网站A产生Cookie信息并返回给浏览器,此时用户登录网站A成功,可以正常发送请求到网站A;
3. 用户未退出网站A之前,在同一浏览器中,打开一个TAB页访问网站B;
4. 网站B接收到用户请求后,返回一些攻击性代码,并发出一个请求要求访问第三方站点A;
5. 浏览器在接收到这些攻击性代码后,根据网站B的请求,在用户不知情的情况下携带Cookie信息,向网站A发出请求。网站A并不知道该请求其实是由B发起的,所以会根据用户C的Cookie信息以C的权限处理该请求,导致来自网站B的恶意代码被执行。
二、CSRF漏洞防御
CSRF漏洞防御主要可以从三个层面进行,即服务端的防御、用户端的防御和安全设备的防御。
1、 服务端的防御
.1.1 验证HTTP Referer字段
根据HTTP协议,在HTTP头中有一个字段叫Referer,它记录了该HTTP请求的来源地址。在通常情况下,访问一个安全受限页面的请求必须来自于同一个网站。比如某银行的转账是通过用户访问http://bank.test/test?page=10&userID=101&money=10000页面完成,用户必须先登录bank.test,然后通过点击页面上的按钮来触发转账事件。当用户提交请求时,该转账请求的Referer值就会是转账按钮所在页面的URL(本例中,通常是以bank. test域名开头的地址)。而如果攻击者要对银行网站实施CSRF攻击,他只能在自己的网站构造请求,当用户通过攻击者的网站发送请求到银行时,该请求的Referer是指向攻击者的网站。因此,要防御CSRF攻击,银行网站只需要对于每一个转账请求验证其Referer值,如果是以bank. test开头的域名,则说明该请求是来自银行网站自己的请求,是合法的。如果Referer是其他网站的话,就有可能是CSRF攻击,则拒绝该请求。
1.2 在请求地址中添加token并验证
CSRF攻击之所以能够成功,是因为攻击者可以伪造用户的请求,该请求中所有的用户验证信息都存在于Cookie中,因此攻击者可以在不知道这些验证信息的情况下直接利用用户自己的Cookie来通过安全验证。由此可知,抵御CSRF攻击的关键在于:在请求中放入攻击者所不能伪造的信息,并且该信息不存在于Cookie之中。鉴于此,系统开发者可以在HTTP请求中以参数的形式加入一个随机产生的token,并在服务器端建立一个拦截器来验证这个token,如果请求中没有token或者token内容不正确,则认为可能是CSRF攻击而拒绝该请求。
1.3 在HTTP头中自定义属性并验证
自定义属性的方法也是使用token并进行验证,和前一种方法不同的是,这里并不是把token以参数的形式置于HTTP请求之中,而是把它放到HTTP头中自定义的属性里。通过XMLHttpRequest这个类,可以一次性给所有该类请求加上csrftoken这个HTTP头属性,并把token值放入其中。这样解决了前一种方法在请求中加入token的不便,同时,通过这个类请求的地址不会被记录到浏览器的地址栏,也不用担心token会通过Referer泄露到其他网站。
2、 其他防御方法
1. CSRF攻击是有条件的,当用户访问恶意链接时,认证的cookie仍然有效,所以当用户关闭页面时要及时清除认证cookie,对支持TAB模式(新标签打开网页)的浏览器尤为重要。
2. 尽量少用或不要用request()类变量,获取参数指定request.form()还是request. querystring (),这样有利于阻止CSRF漏洞攻击,此方法只不能完全防御CSRF攻击,只是一定程度上增加了攻击的难度。
代码示例:
Java 代码示例
下文将以 Java 为例,对上述三种方法分别用代码进行示例。无论使用何种方法,在服务器端的拦截器必不可少,它将负责检查到来的请求是否符合要求,然后视结果而决定是否继续请求或者丢弃。在 Java 中,拦截器是由 Filter 来实现的。我们可以编写一个 Filter,并在 web.xml 中对其进行配置,使其对于访问所有需要 CSRF 保护的资源的请求进行拦截。
在 filter 中对请求的 Referer 验证代码如下
清单 1. 在 Filter 中验证 Referer
// 从 HTTP 头中取得 Referer 值String referer=request.getHeader("Referer");// 判断 Referer 是否以 bank.example 开头if((referer!=null)&&(referer.trim().startsWith(“bank.example”))){ chain.doFilter(request, response);}else{ request.getRequestDispatcher(“error.jsp”).forward(request,response);}
以上代码先取得 Referer 值,然后进行判断,当其非空并以 bank.example 开头时,则继续请求,否则的话可能是 CSRF 攻击,转到 error.jsp 页面。
如果要进一步验证请求中的 token 值,代码如下
清单 2. 在 filter 中验证请求中的 token
HttpServletRequest req =(HttpServletRequest)request;HttpSession s = req.getSession();// 从 session 中得到 csrftoken 属性String sToken =(String)s.getAttribute(“csrftoken”);if(sToken ==null){// 产生新的 token 放入 session 中 sToken = generateToken(); s.setAttribute(“csrftoken”,sToken); chain.doFilter(request, response);}else{// 从 HTTP 头中取得 csrftokenString xhrToken = req.getHeader(“csrftoken”);// 从请求参数中取得 csrftokenString pToken = req.getParameter(“csrftoken”);if(sToken !=null&& xhrToken !=null&& sToken.equals(xhrToken)){ chain.doFilter(request, response);}elseif(sToken !=null&& pToken !=null&& sToken.equals(pToken)){ chain.doFilter(request, response);}else{ request.getRequestDispatcher(“error.jsp”).forward(request,response);}}
首先判断 session 中有没有 csrftoken,如果没有,则认为是第一次访问,session 是新建立的,这时生成一个新的 token,放于 session 之中,并继续执行请求。如果 session 中已经有 csrftoken,则说明用户已经与服务器之间建立了一个活跃的 session,这时要看这个请求中有没有同时附带这个 token,由于请求可能来自于常规的访问或是 XMLHttpRequest 异步访问,我们分别尝试从请求中获取 csrftoken 参数以及从 HTTP 头中获取 csrftoken 自定义属性并与 session 中的值进行比较,只要有一个地方带有有效 token,就判定请求合法,可以继续执行,否则就转到错误页面。生成 token 有很多种方法,任何的随机算法都可以使用,Java 的 UUID 类也是一个不错的选择。
除了在服务器端利用 filter 来验证 token 的值以外,我们还需要在客户端给每个请求附加上这个 token,这是利用 js 来给 html 中的链接和表单请求地址附加 csrftoken 代码,其中已定义 token 为全局变量,其值可以从 session 中得到。
清单 3. 在客户端对于请求附加 token
function appendToken(){ updateForms(); updateTags();} function updateForms(){// 得到页面中所有的 form 元素 var forms = document.getElementsByTagName('form');for(i=0; i<forms.length; i++){ var url = forms[i].action;// 如果这个 form 的 action 值为空,则不附加 csrftokenif(url ==null|| url =="")continue;// 动态生成 input 元素,加入到 form 之后 var e = document.createElement("input"); e.name ="csrftoken"; e.value = token; e.type="hidden"; forms[i].appendChild(e);}} function updateTags(){ var all = document.getElementsByTagName('a'); var len = all.length;// 遍历所有 a 元素for(var i=0; i<len; i++){ var e = all[i]; updateTag(e,'href', token);}} function updateTag(element, attr, token){ var location = element.getAttribute(attr);if(location !=null&& location !=''''){ var fragmentIndex = location.indexOf('#'); var fragment =null;if(fragmentIndex !=-1){//url 中含有只相当页的锚标记 fragment = location.substring(fragmentIndex); location = location.substring(0,fragmentIndex);} var index = location.indexOf('?');if(index !=-1){//url 中已含有其他参数 location = location +'&csrftoken='+ token;}else{//url 中没有其他参数 location = location +'?csrftoken='+ token;}if(fragment !=null){ location += fragment;} element.setAttribute(attr, location);}}
在客户端 html 中,主要是有两个地方需要加上 token,一个是表单 form,另一个就是链接 a。这段代码首先遍历所有的 form,在 form 最后添加一隐藏字段,把 csrftoken 放入其中。然后,代码遍历所有的链接标记 a,在其 href 属性中加入 csrftoken 参数。注意对于 a.href 来说,可能该属性已经有参数,或者有锚标记。因此需要分情况讨论,以不同的格式把 csrftoken 加入其中。
如果你的网站使用 XMLHttpRequest,那么还需要在 HTTP 头中自定义 csrftoken 属性,利用 dojo.xhr 给 XMLHttpRequest 加上自定义属性代码如下:
清单 4. 在 HTTP 头中自定义属性
var plainXhr = dojo.xhr;// 重写 dojo.xhr 方法 dojo.xhr = function(method,args,hasBody){// 确保 header 对象存在 args.headers = args.header ||{}; tokenValue ='<%=request.getSession(false).getAttribute("csrftoken")%>'; var token = dojo.getObject("tokenValue"); // 把 csrftoken 属性放到头中 args.headers["csrftoken"]=(token)? token :" ";return plainXhr(method,args,hasBody);};
这里改写了 dojo.xhr 的方法,首先确保 dojo.xhr 中存在 HTTP 头,然后在 args.headers 中添加 csrftoken 字段,并把 token 值从 session 里拿出放入字段中。
PHP代码示例:
请看下面一个简单的应用,它允许用户购买钢笔或铅笔。界面上包含下面的表单:
<formaction="buy.php"method="POST"><p> Item: <selectname="item"><optionname="pen">pen</option><optionname="pencil">pencil</option></select><br/> Quantity: <inputtype="text"name="quantity"/><br/><inputtype="submit"value="Buy"/></p></form>
下面的buy.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>';}}?>
攻击者会首先使用这个表单来观察它的动作。例如,在购买了一支铅笔后,攻击者知道了在购买成功后会出现感谢信息。注意到这一点后,攻击者会尝试通过访问下面的URL以用GET方式提交数据是否能达到同样的目的:
http://store.example.org/buy.php?item=pen&quantity=1
如果能成功的话,攻击者现在就取得了当合法用户访问时,可以引发购买的URL格式。在这种情况下,进行跨站请求伪造攻击非常容易,因为攻击者只要引发受害者访问该URL即可。
请看下面对前例应用更改后的代码:
php session_start(); $token = md5(uniqid(rand(), TRUE)); $_SESSION['token']= $token; $_SESSION['token_time']= time();?>
表单:
<formaction="buy.php"method="POST"> <input type="hidden" name="token" value="<?php echo $token;?>" /> <p> Item: <selectname="item"><optionname="pen">pen</option><optionname="pencil">pencil</option></select><br/> Quantity: <inputtype="text"name="quantity"/><br/><inputtype="submit"value="Buy"/></p></form>
通过这些简单的修改,一个跨站请求伪造攻击就必须包括一个合法的验证码以完全模仿表单提交。由于验证码的保存在用户的session中的,攻击者必须对每个受害者使用不同的验证码。这样就有效的限制了对一个用户的任何攻击,它要求攻击者获取另外一个用户的合法验证码。使用你自己的验证码来伪造另外一个用户的请求是无效的。
该验证码可以简单地通过一个条件表达式来进行检查:
<?php if(isset($_SESSION['token'])&& $_POST['token']== $_SESSION['token']){/* Valid Token */}?>
你还能对验证码加上一个有效时间限制,如5分钟:
<?php $token_age = time()- $_SESSION['token_time'];if($token_age <=300){/* Less than five minutes has passed. */}?>
通过在你的表单中包括验证码,你事实上已经消除了跨站请求伪造攻击的风险。可以在任何需要执行操作的任何表单中使用这个流程。
相关推荐
Tomcat 防止跨站请求伪造(CSRF)机制浅析 在 Web 应用开发中,跨站请求伪造(CSRF)是一种常见的安全威胁。跨站请求伪造攻击是指攻击者诱骗受信任用户访问恶意网站,从而使得恶意网站能以用户身份对受信任网站执行...
跨站请求伪造-CSRF防护方法 CSRF(Cross-Site Request Forgery,跨站请求伪造)是一种常见的Web攻击方式,攻击者可以欺骗用户浏览器,发出恶意请求,危害用户的安全。因此,防御CSRF攻击非常重要。下面将对CSRF攻击...
CSRF(Cross-Site Request Forgery,跨站请求伪造)是一种网络攻击方式,攻击者通过诱导用户在已登录的网站上执行非预期的操作。在这个PHP demo中,我们将深入理解CSRF攻击的原理,并探讨如何防范。 首先,让我们看...
标题中的“防止伪造跨站请求”指的是Web应用安全领域中的CSRF(Cross-Site Request Forgery,跨站请求伪造)防护。CSRF攻击是利用用户的已登录状态,诱使用户在不知情的情况下执行非预期的操作,例如转移资金、更改...
CSRF全称Cross-site request forgery(跨站请求伪造),是一种网络的攻击方式,也被称为“One Click Attack”或者Session Riding,通常缩写为CSRF或者XSRF。 攻击原理 1、用户访问正常的网站A,浏览器就会保存网站A...
在Laravel框架中,表单请求类型和CSRF(跨站请求伪造)防护是Web应用开发中的关键概念,确保了应用程序的安全性和用户交互的正确性。以下是对这些知识点的详细说明: 1. **表单请求类型**: Laravel提供了一组便捷...
在这个实验中,我们将探讨一个重要的安全问题——跨站请求伪造(Cross-Site Request Forgery, CSRF)攻击,这种攻击允许恶意用户在受害者不知情的情况下,利用其浏览器发起伪造的请求。通过这个实验,你可以了解CSRF...
跨站点请求伪造(Cross-Site Request Forgery,简称 CSRF 或 XSRF)是一种网络攻击方式,它利用了用户浏览器已经存储的登录凭证,如Cookie,来执行非授权的操作。这种攻击常见于Web应用中,使得攻击者能够在用户不...
通常情况下,这可能是测试过程中记录的错误日志或异常信息,它可以帮助开发者调试代码,了解CSRF防护是否存在问题以及可能的解决方案。 总结来说,防止CSRF攻击是Web应用安全的重要环节,Spring框架提供了完善的...
CSRF(Cross-Site Request Forgery,跨站请求伪造)是一种网络攻击方式,也可以说是一种安全漏洞,这种安全漏洞在web开发中广泛存在。在SpringSecurity框架下,实现CSRF跨站攻击防御是非常重要的。本文将详细介绍...
【标题】"Exploit-DMA-Radius-Manager-4.4.0---Cross-Site-Request-Forgery-CSRF-"揭示了一个针对DMA Radius Manager 4.4.0版本的安全漏洞,该漏洞涉及到跨站请求伪造(CSRF)攻击。这是一种网络安全威胁,允许攻击...
CSRF(Cross-Site Request Forgery,跨站点请求伪造)是一种常见的 web 攻击方式,攻击者可以盗用用户的身份,以用户的名义发送恶意请求,对服务器来说这个请求是完全合法的,但却完成了攻击者所期望的一个操作。...
Django框架在处理跨域请求(Cross-Origin Resource Sharing, CORS)时,面临CSRF(Cross-Site Request Forgery,跨站请求伪造)的安全风险。本文将探讨Django如何安全地实现跨域请求同时防范CSRF攻击,并提供相关的...
跨站请求伪造(CSRF)和跨站脚本(XSS)是两种常见的网络攻击方式,它们可以对用户的安全和隐私造成严重威胁。本文将详细探讨CSRF和XSS攻击的原理、特点以及在Java Web应用中的防护策略。 CSRF和XSS攻击是Web应用...
**Web应用安全:CSRF(跨站点请求伪造)防范对策** **一、CSRF的定义** CSRF(Cross Site Request Forgery),中文称为跨站点请求伪造,是一种恶意攻击手段,攻击者利用用户已登录的身份,诱导用户访问含有恶意请求...
CSRF(Cross-site request forgery),即跨站请求伪造,是一种常见的 Web 攻击。Spring Security 4.0 及更高版本默认启用 CSRF 防护,以防止 CSRF 攻击应用程序。下面是 Spring Security CSRF 防护机制的详细解释。 ...