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程序处理表单的提交信息:

<?phpsession_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即可。

请看下面对前例应用更改后的代码:

phpsession_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中的,攻击者必须对每个受害者使用不同的验证码。这样就有效的限制了对一个用户的任何攻击,它要求攻击者获取另外一个用户的合法验证码。使用你自己的验证码来伪造另外一个用户的请求是无效的。

该验证码可以简单地通过一个条件表达式来进行检查:

<?phpif(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. */}?>

通过在你的表单中包括验证码,你事实上已经消除了跨站请求伪造攻击的风险。可以在任何需要执行操作的任何表单中使用这个流程。

【编辑推荐】

  1. 五大方法减少跨站请求伪造(CSRF)攻击
  2. 浅谈CSRF攻击方式
  3. 浅谈CSRF漏洞挖掘技巧

转载于:https://www.cnblogs.com/soul-mate/p/6837668.html

跨站请求伪造CSRF防护方法相关推荐

  1. 跨站请求伪造(CSRF)-简述

    跨站请求伪造(CSRF)-简述 跨站请求伪造(英语:Cross-site request forgery),也被称为 one-click attack 或者 session riding,通常缩写为 ...

  2. 带你刷burpsuite官方网络安全学院靶场(练兵场)之客户端漏洞——跨站请求伪造(CSRF)专题

    介绍 PortSwigger是信息安全从业者必备工具burpsuite的发行商,作为网络空间安全的领导者,他们为信息安全初学者提供了一个在线的网络安全学院(也称练兵场),在讲解相关漏洞的同时还配套了相 ...

  3. django16: csrf跨站请求伪造/CSRF相关装饰器

    CSRF 即跨站请求攻击 跨站请求伪造csrf钓鱼网站本质搭建一个跟正常网站一模一样的页面用户在该页面上完成转账功能转账的请求确实是朝着正常网站的服务端提交唯一不同的在于收款账户人不同给用户书写for ...

  4. csrf防御 php,跨站请求伪造CSRF的防御实例(PHP版本)

    跨站请求伪造CSRF的防御:One-Time Tokens(不同的表单包含一个不同的伪随机值) 在实现One-Time Tokens时,需要注意一点:就是"并行会话的兼容".如果用 ...

  5. 浅谈跨站请求伪造(CSRF)

    浅谈跨站请求伪造(CSRF)   这里简单的记录一下CSRF漏洞~~ 什么是CSRF?   CSRF(Cross-Site Request Forgery,跨站点伪造请求)是一种网络攻击方式,该攻击可 ...

  6. Nginx配置valid_referer解决跨站请求伪造(CSRF)

    Nginx配置valid_referer解决跨站请求伪造(CSRF) 文章目录 Nginx配置valid_referer解决跨站请求伪造(CSRF) 漏洞说明 漏洞描述 危害等级 修复建议 漏洞复现 ...

  7. 跨站请求伪造(CSRF)+ 跨站脚本攻击(XSS)

    一.CSRF 跨站请求伪造(英语:Cross-site request forgery),也被称为 one-click attack 或者 session riding,通常缩写为 CSRF 或者 X ...

  8. 跨站请求伪造——CSRF

    目录 csrf与xss的区别 CSRF的介绍 利用过程 实战 靶场下载 过程 一.A用户登录浏览器执行动作 二.B用户抓包制作csrf请求页面 三.本地保存 四.制作钓鱼网站 csrf 防御 csrf ...

  9. Web安全测试---跨站请求伪造CSRF

    跨站请求 伪造 (即CSRF )被Web 安全 界称为诸多漏洞中"沉睡的巨人",其威胁程度由此"美誉"便可见一斑.本文将简单介绍该漏洞,并详细说明造成这种漏洞的 ...

最新文章

  1. Springboot 日志、配置文件、接口数据如何脱敏?老鸟们都是这样玩的!
  2. javascript运行时问题
  3. 怎样得到select所有option里的值
  4. web developer tips (28):CSS class 也支持“转到定义”
  5. QGLViewer 编译安装步骤
  6. 分布式事务中常见的三种解决方案
  7. Linux查看占用mem的进程脚本
  8. 拓端tecdat|R语言极值推断:广义帕累托分布GPD使用极大似然估计、轮廓似然估计、Delta法
  9. mysql load data 一行_MySQL LOAD DATA LOCAL INFILE仅导入一行
  10. 猿创征文|C++软件开发值得推荐的十大高效软件分析工具
  11. i9x系列是服务器CPU吗,Intel推出全新酷睿X系列CPU:i9处理器亮相
  12. Nemty 勒索软件代码中包含对杀毒软件公司的强烈措辞
  13. 第二十二章 : 格式化输出
  14. jupyter 或者 zeppelin 的下一代工具 polynote
  15. ppt制作教程与原理介绍(学习记录)
  16. 键盘侠Linux教程(一)| 初学者建议
  17. 对于店铺违规降权,究竟该何去何从
  18. 微信小程序人工智能之添加学生信息
  19. Java计算器(带Gui)
  20. LeetCode每日一题——927. 三等分

热门文章

  1. 霸榜 | 微软CV模型收获近 2k star
  2. 经典重温:CVPR 2010 What is an object?
  3. Facebook田渊栋谈人生挑战与选择
  4. Github | PyTorch实现的深度强化学习算法集
  5. 什么!卷积要旋转180度?!
  6. 天涯明月刀开发_《天涯明月刀手游》正式上线 4700万玩家期待的那个江湖来了...
  7. oracle 源代码输出,oracle-如何将DBMS_OUTPUT.PUT_LINE的输出重定向到文件?
  8. 走进android的世界(不断更新中)
  9. python treeview显示多列_Python Gtk TreeView列数据显示
  10. vue为基座微前端嵌入html,使用Vue CSR的微前端实现方案实践