CSRF攻击
CSRF攻击全称为:Cross-site request forgery,直接翻译为:跨站请求伪造。直接看名称还是有点难以理解,容易跟XSS攻击搞混。在讲解如何防御之前,首先看看如何攻击,举个简单的攻击例子:
1、假设你知道身边的一个同事每天都会登陆他的xxx网上银行(假设这个银行没有做CSRF防御),由于习惯他一般会采用默认的浏览器登陆;
2、在他登陆网上银行之后,你往他的邮箱发一封邮件(他是你同事你当然知道他的邮箱),邮件的内容为一张图片(图片内容要足够吸引人去点击,比如“京东商城”满199-100什么的),图片的链接地址为xxx网上银行的转账操作链接比如:https://xxx.bank.com/ trans_money? money=10000&target_accuout=“你的银行账户”,该链接执行的操作是向你的账户转10000块。
3、你的同事收到邮件后,点击这个图片,会使用默认浏览器打开上述转账链接。由于他已经使用默认浏览器登陆了自己的网上银行,这时就会在你同事毫无知觉的情况下,借他的手向你的账户转10000块大洋(别转太多了,太多了需要短信验证什么的)。
上述攻击示例仅仅是为了说明CSRF的攻击方式,请勿尝试 不要拿自己的同事做猎物。当然现在的网上银行不会像我说的这么弱智,即便尝试也没有效果。
这里举例是用邮件向攻击目标推送“攻击链接”,也可以使用任意的其他方式,比起在其他网站上挂一个“攻击链接”,如果他在登陆自己“网上银行”的浏览器里同时打开了这个“其他网站”,你又成功的引诱他点击了这个“攻击链接”,就可以接攻击目标自己的手执行你想要的任何操作。这就是所谓的CSRF攻击,可见其危害之大。
攻击者一般会在通过扫描工具扫描系统是否存在CSRF漏洞的操作链接,然后分析这些链接是否有价值,比如删除或修改重要数据、发送邮件等。然后构造这些链接请求,在目标用户登陆该系统的情况下,通过各种手段诱导你去执行这些链接,从而达到自己的攻击目的。
CSRF防御
CSRF防御比较常见的手段是对每个请求进行token验证,对验证不通过的请求进行拦截。这种方式理论可以对每个请求都进行token验证,但这样系统就缺乏一些灵活性,根据具体情况,一般不会对所有的请求进行token校验,只对有数据更改的部分(post请求)或者敏感数据查询进行token校验。token验证流程如下:
1、客户端发起请求浏览一个页面,服务端收到请求 通过UUID生成一个随机数作为token,存放在服务器端,现在的系统一般都是多实例分布式部署,所有一般采用共享缓存进行存储,比如redis(为什么不使用session、request或者本地缓存?因为下一次请求有可能落到另外一台机器上)。
2、服务端渲染页面返回时,把这个新生成的token放到一个hidden的隐藏变量中。
3、客户端在请求或修改敏感数据时,在请求header中附带上这个token。服务端收到请求后,获取header中的token,与共享缓存中的token进行对比:
A、假设两个token相同,则通过验证,为了防止表单重复提交,这时可以在“共享缓存”中删除这个token。然后继续进行正常业务处理,在请求返回之前生成新token存放到“共享缓存”,连同该新token一起返回给客户端,以便后续请求继续使用。
B、假设两个token不相同,说明有可能是token已过期(“共享缓存”中的token不能无限期的存放,一般半个小时左右即可),或者是遭到了CSRF攻击。这时拦截该请求,返回请求失败。如果是token已过期,可以刷新页面获取新的token 即可继续操作,这也就是为什么有的网站在过一段时间直接需要刷新一下才能继续操作的原因。
可以看到这个token是实时变化的,CSRF攻击者无法进行伪造,从而达到防御的目的。
Spring MVC中的CSRF防御
通过上述流程,可以看到CSRF防御的关键就是Token的生成、删除和验证,这些操作都是在正常的业务操作之前或者之后进行的(验证和删除是之前,生成是之后)。在Spring MVC中很容易就能想到通过拦截器HandlerInterceptor进行处理(如果对spring拦截器不清楚的,可以点击这里)。具体的处理方式,根据链接是否有规律又分为两种:
拦截器统一处理方式:对链接有规律的处理比较简单(比如RESTful风格的链接),只需要对固定的链接进行链接,在preHandle中进行token的“验证和删除”,在postHandle中进行token的“生成”即可,这也是使用RESTful风格编程的福利:
拦截器xml配置:
<mvc:interceptor> <mvc:mapping path="/xx/delete"/> <mvc:mapping path="/xx/update"/> <bean class="com.xxx.intercepter.CSRFInterceptor" /> </mvc:interceptor>
CSRFInterceptor拦截器实现:
public class CSRFInterceptor implements HandlerInterceptor { @Resource private CSRFTokenUtil csrfTokenUtil; //验证和删除token @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String requstCSRFToken = request.getHeader("csrf-token"); if(csrfTokenUtil.verifyToken(requstCSRFToken)){ csrfTokenUtil.deleteToken(requstCSRFToken);//验证通过后,立即删除token,可以表单防止重复提交。 return true; } return false; } //生成token @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { String new_token = csrfTokenUtil.generate(); request.setAttribute("csrf-token", new_token); //页面上后续异步操作,需要新的token } //省略afterCompletion方法 }
另外还需要在,访问指定页面时,生成token
访问页面的请求生成token:
@RequestMapping("/form") @ VerifyCSRFToken @ResponseBody public String form (Map map,Integer id) { //省略业务代码 String new_token = csrfTokenUtil.generate(); map.put("csrf-token",new_token);//生成token return “/form” }
ok,大功告成,可见如果采用spring mvc的RESTful风格编程,对防御CSRF攻击是so eazy。
指定注解方式:但不幸的是我们有许多老系统,不是RESTful风格的,链接的规则也是杂乱无章,肿么办。这时可以采用拦截器加注解的方式,进行处理,处理起来稍微麻烦些,分三步说明:
1、创建拦截器,拦截所有的请求:
<mvc:interceptor> <mvc:mapping path="/**"/> <bean class="com.xxx.intercepter.CSRFInterceptor" /> </mvc:interceptor>
2、新建一个自定义注解VerifyCSRFToken,加到需要进行token验证的Controller方法中,并在这个方法返回之前,生成新token。
VerifyCSRFToken注解定义:
@Target({ java.lang.annotation.ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) public @interface VerifyCSRFToken { //需要验证防跨站请求 public abstract boolean verify() default true; }
访问页面的请求生成token:
@RequestMapping("/form") @ VerifyCSRFToken @ResponseBody public String form (Map map,Integer id) { //省略业务代码 String new_token = csrfTokenUtil.generate(); map.put("csrf-token",new_token);//生成token return “/form” }
需要进行防御的方法:
@RequestMapping("/update") @ VerifyCSRFToken @ResponseBody public void update (Integer id) { //省略业务代码 String new_token = csrfTokenUtil.generate(); result. put("csrf-token",new_token); //重新生成token sendResultJson(result); }
3、最后看下拦截器的处理,由于token的生成已经分散到各个Cotrlloer方法中,拦截器的postHandle无需处理。由于拦截器CSRFInterceptor拦截了所有的请求,在preHandle需要首先取出含有@ VerifyCSRFToken的方法,才能进行token校验。具体实现如下:
public class CSRFInterceptor implements HandlerInterceptor { @Resource private CSRFTokenUtil csrfTokenUtil; //验证和删除token @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HandlerMethod handlerMethod = (HandlerMethod) handler; Method method = handlerMethod.getMethod(); VerifyCSRFToken verifyCSRFToken = method.getAnnotation(VerifyCSRFToken.class); // 只对使用@VerifyCSRFToken注解的方法,进行csrf token校验 if (verifyCSRFToken != null) { String requstCSRFToken = request.getHeader("csrf-token"); if (csrfTokenUtil.verifyToken(requstCSRFToken)) { csrfTokenUtil.deleteToken(requstCSRFToken);//验证通过后,立即删除token,可以表单防止重复提交。 return true; } return false; } return true; } //生成token @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { } //省略afterCompletion方法 }
对比两种方式:“拦截器统一处理方式”看起来更优雅,对业务没有侵入性,但链接必须是规则的RESTful风格。“指定注解方式”使用更灵活,可以对指定的方法进行防御,但对业务代码有一定的侵入性。两种方式可以根据自己的系统具体情况进行选择。
token工具类
前面代码实例中用的了CSRF的工具类CSRFTokenUtil,这个工具类封装了token的生成、校验、删除。前面也提到过,在分布式的部署系统中,只能使用“共享缓存”进行token在服务端的存在,本工具类使用的是redis。
另外,通过前面的代码我们会发现,每次访问一个新页面时都需要生成一个新的token放到redis,如果有恶意用户 一直刷新页面(多机并发刷),理论上会到导致redis缓存被刷爆。所有我们必须对每个用的token数量进行限制,但又不能太少,否则用户不能同时打开多个页面。这里我们限制每个用户,最多只能生产100个token,如果超过100不再生产新的token,而是随机选择这个100个中的一个token返回,采用的是redis的set(集合)数据类型进行存储(提示:下列代码中的redis的操作 进行过封装,请使用自己的项目中redis的使用方式替换),这样可以防止redis 恶意被刷,实现代码如下:
/** * csrf攻击防御工具类 * Created by gantianxing on 2017/10/13. */ public class CSRFTokenUtil { public static final String CSRF_TOKEN="csrf-token"; public static final int THIRTY_MINUTES = 30*60;//token缓存时间30分钟 @Resource private RedisUtil redis; /** * 生成新token 放入redis set中(集合) * 每个user最多允许100个token * @return */ public String generate() { int userId = getUserId(); if(userId > 0){ String key = "user_token"+userId; int snum = redis.scard(key); //如果该用户的token数大于100,则随机返回一个已有token,不在生成新token if(snum > 100){ token = redis.srandmenber(key); }else {//否则生成新token String uuid = UUID.randomUUID().toString(); redis.sadd(key,THIRTY_MINUTES,uuid); } } return token; } /** * 验证token(在set中查找) * @param page_token * @return */ public boolean verifyToken(String page_token){ if(redis.isNotBlank(page_token)){ int userId = getUserId(); String key = "user_token"+userId; //判断redis集合中是否存在 if(userId>0 && redis.sismember(key,page_token)){ return true; } } return false; } /** * 删除token(从set中删除) * @param page_token */ public void deleteToken(String page_token){ int userId = getUserId(); if (userId>0){ String key = "user_token"+userId; redis.srem(key, page_token);//从redis集合中删除 } } //获取当前用户id private int getUserId(){ //获取用户id逻辑省略,一般会把用户信息放到TreadLocal中,从TreadLocal中获取 return 123; } }
关于token的工具类的编写,主要核心有两点:1、使用支持分布式的“共享缓存” 2、token要遵守谁创建谁使用的原则(就是跟用户绑定),同时必须限制每个用户创建token数量。
对于CSRF攻击方式,以及如何防御就总结到这里。
出处:
http://moon-walker.iteye.com/blog/2397904
相关推荐
主要介绍了详解如何在spring boot中使用spring security防止CSRF攻击,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
浅谈CSRF攻击方式浅谈CSRF攻击方式浅谈CSRF攻击方式浅谈CSRF攻击方式浅谈CSRF攻击方式
使用CSRF攻击破解
你这可以这么理解CSRF攻击:攻击者盗用了你的身份,以你的名义发送恶意请求。CSRF能够做的事情包括:以你名义发送邮件,发消息,盗取你的账号,甚至于购买商品,虚拟货币转账......造成的问题包括:个人隐私泄露...
主要介绍了使用SpringSecurity处理CSRF攻击的方法步骤,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
CSrf攻击-苏醒的巨人
主要介绍了spring security中的csrf防御机制原理解析(跨域请求伪造),本文通过实例代码详解的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
如何防御CSRF攻击.zip
CSRF漏洞防御-01
上图为CSRF攻击的一个简单模型,用户访问恶意网站B,恶意网站B返回给用户的HTTP信息中要求用户访问网站A,而由于用户和网站A之间可能已经有信任关系导致这个请求就像用户真实发送的一样会被执行。攻击者盗用了你的...
CSRF攻击手段与影响 CSRF攻击手段与影响 1、攻击流程 步骤一:用户登录、浏览并信任正规网站WebA,同时,WebA通过用户的验证并在用户的浏览器中产生Cookie。 CSRF攻击手段与影响 1、攻击流程 步骤二:攻击者WebB通过...
主要介绍了SpringSecurity的防Csrf攻击实现代码解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
bwapp平台上的CSRF攻击演示代码。
CSRF 背景与介绍 CSRF 攻击实例 CSRF 攻击的对象 CSRF 攻击的对象 Java 代码示例
spring-webmvc-pac4j, 面向 spring Web MVC的安全 spring-webmvc-pac4j 项目是一个为 Web MVC ( 带或者不带 spring 引导) Web应用程序提供的简单安全安全库。 它支持认证和授权,但还支持注销和高级特性,比如会话...
应对web应用中的CSRF攻击的对应之策
CSRF是一种网络攻击方式,也可以说是一种安全漏洞,这种安全漏洞在web开发中广泛存在。这篇文章主要介绍了SpringSecurity框架下实现CSRF跨站攻击防御,需要的朋友可以参考下
针对Breach攻击的CSRF防御模块的设计与实现,刘赟,郭燕慧,随着越来越多的真实Web世界的CSRF攻击事件的发生,更多的安全工程师开始重视起这个原本被忽略的攻击方式,新出现的BREACH攻击也对目�
简解:CSRF的原理及防御
- Spring MVC - Spring Data - Spring Security - Spring Cloud 4. Web开发: - HTML、CSS、JavaScript - HTTP协议 - Servlet、JSP - AJAX、JSON、XML 5. 框架和工具: - MyBatis - Hibernate - ...