【Java代码审计】CSRF篇
- 1.CSRF漏洞概述
- 2.对 Referer 过滤不严导致的 CSRF 漏洞
- 3.token 可重用导致 CSRF 漏洞
- 4.CSRF 漏洞的防御
1.CSRF漏洞概述
CSRF(Cross Site Request Forgery,跨站点请求伪造)是目前出现次数比较多的漏洞,该漏洞能够使攻击者盗用被攻击者的身份信息,去执行相关敏感操作。实际上这种方式是攻击者通过一些钓鱼等手段欺骗用户去访问一个自己曾经认证过的网站,然后执行一些操作(如后台管理、发消息、添加关注甚至是转账等行为)简而言之,CSRF 漏洞的工作原理是攻击者盗用了用户的身份,以用户的名义发送恶意请求。
一次完整的 CSRF 攻击需要具备以下两个条件
- 用户已经登录某站点,并且在浏览器中存储了登录后的 Cookie 信息
- 在不注销某站点的情况下,去访问攻击者构造的站点
CSRF 攻击可能出现的场景有很多,如更改个人信息、添加/修改资料、关注用户或者与交易相关的操作等。CSRF 漏洞的出现通常是由于开发人员对该类型漏洞不了解,因而疏忽了对该类型漏洞的防范。
通常来说,检测 CSRF 漏洞是一项比较烦琐的工作,最简单的方法就是抓取一个正常请求的 GET/POST 数据包,删除 Referer 字段后再重新提交,如果该提交操作有效,那么基本上可以确定该操作存在 CSRF 漏洞
通过代码审计去挖掘 CSRF 漏洞,一般需要首先了解该开源程序的框架。CSRF 漏洞一般会在框架中存在防护方案,所以在审计 CSRF漏洞时,首先要熟悉框架对 CSRF 的防护方案,若没有防护方案,则以该框架编写的所有Web 程序都可能存在 CSRF 漏洞;若有防护方案,则可以首先去查看增删改请求中是否有 token、formtoken、csrf-token 等关键字,若有则可以进一步去通读该 Web程序对 CSRF 的防护源码,来判断其是否存在替换 token 值为自定义值并重复请求漏洞、重复使用 token 等漏洞。此外还要关注源程序是否对请求的 Referer 进行校验等。
2.对 Referer 过滤不严导致的 CSRF 漏洞
public class RefererInterceptor implements HandlerInterceptor {private boolean check = true;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {// 如果检查标志为 false,则直接放行if (!check) {return true;}// 获取请求的 Referer 头部信息String referer = request.getHeader("Referer");// 如果 Referer 存在并且以指定域名开头,则放行if (referer != null && referer.trim().startsWith("www.testdomain.com")) {return true;} else {// 否则,重定向到首页或其他页面request.getRequestDispatcher("index.jsp").forward(request, response);return false;}}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,ModelAndView modelAndView) throws Exception {// 在处理请求之后,渲染视图之前执行的操作}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,Exception ex) throws Exception {// 在完成整个请求之后,清理资源等}
}
这里逻辑判断的关键点在于第二步。该判断仅仅判断请求 Referer 字段是否以 www.testdomain.com
开头 ,若我们构建一个二级域名为 www.testdomain.com.hacker.com 的地址,则可能成功绕过该判断,从而进行 CSRF 攻击
3.token 可重用导致 CSRF 漏洞
public class CsrfTokenInterceptor extends HandlerInterceptorAdapter {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 生成服务器端的 CSRF 令牌String sToken = generateToken();// 获取请求参数中的 CSRF 令牌String pToken = request.getParameter("csrf-token");// 如果服务器端的令牌和请求参数中的令牌都不为空,并且它们相等,则放行if (sToken != null && pToken != null && sToken.equals(pToken)) {return true;} else {// 否则,重定向到首页或其他页面request.getRequestDispatcher("index.jsp").forward(request, response);return false;}}
}
在这段程序中,当用户登录成功后,首先通过 generateToken()
方法生成一个属于该用户的 token,然后将其保存在服务端,并且将其镶嵌到 HTML 页面中的<input>
标签内。当用户提交操作的时候,程序会比对该标签的 token 值是否等于服务端的token 值,如果相等,则判断该操作是用户本人操作,而不是受到了 CSRF 攻击
其实这段源程序对于 CSRF 的防御机制是存在问题的。该段源程序在用户成功登录后,生成了唯一的令牌,直至该用户注销前,该 token 都是有效的。这就可能导致一个问题,如果这个 token 被盗用或者泄露,那么就可能导致CSRF 漏洞的发生
4.CSRF 漏洞的防御
1、当用户发送请求时,服务器端应用将 token 嵌入 HTML 表格中,并发送给客户端。客户端提交HTML 表格,会将令牌发送到服务端,令牌的验证是由服务端实行的,但也要保证 token 不可重用
2、检查 Referer 字段。HTTP 头中有一个 Referer 字段,这个字段用以标明请求来源于哪个地址。在处理敏感数据请求时,一般情况下,Referer 字段应该与请求地址位于同一域名下。而如果是 CSRF 攻击传递来的请求,Referer 字段会是包含恶意攻击载荷的地址
3、验证码机制、自定义 http 请求头方式、Origin 字段等