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
相关推荐
在开发Web应用程序时,安全是至关重要的一个环节。Spring MVC和Spring Security是两个非常流行的Java框架,它们分别...在实际项目中,还需要考虑其他因素,如密码加密、CSRF防护、XSS防御等,以确保全方位的安全性。
登录和注册功能需要考虑的安全问题包括但不限于:密码加密存储,防止SQL注入,XSS攻击防护,CSRF(跨站请求伪造)防御,以及合适的错误消息展示,避免泄露敏感信息。 7. **异常处理**: 使用Spring MVC的异常...
本项目"spring boot xss防御"旨在介绍如何在Spring Boot环境中有效地防止XSS攻击。以下是关于这个主题的详细知识点: 1. XSS攻击类型: - 存储型XSS:攻击者的脚本被存储在服务器端,并在后续请求时传递给其他用户...
为了防止跨站请求伪造(CSRF),在更新操作中通常需要添加 CSRF 防御。 4. **删除(Delete)**:删除数据通常是一个简单的 GET 请求,带有要删除的 ID。控制器方法接收到 ID 后,调用 Service 删除数据库中的对应记录...
Spring Security内置了多种防御机制,如CSRF(跨站请求伪造)防护、XSS(跨站脚本攻击)过滤,以及Session Fixation防护等,增强了应用程序的安全性。开发者可以通过简单配置就能启用这些防护措施,无需编写额外的...
在网络安全领域,XSS(Cross-Site Scripting)攻击是一种常见的威胁,...在实际开发中,还应关注其他安全方面,比如SQL注入、CSRF攻击等,同时遵循OWASP(开放网络应用安全项目)的最佳实践,以提升应用的整体安全性。
5. **CSRF(跨站请求伪造)防护**:Spring Security 自动为受保护的 POST 请求添加 CSRF 防御,确保只有合法的请求才能被执行。 6. **安全配置**:Spring Security 采用声明式配置,可以通过 XML 或者 Java 配置来...
Spring Security致力于防御各种常见的Web攻击,包括SQL注入和跨站脚本攻击(XSS)。它通过限制输入的处理和编码输出来提供额外的安全层。 9. 测试支持(Testing Support): 对于开发高质量的安全应用程序来说,...
6. **CSRF防护(Cross-Site Request Forgery Protection)**:Spring Security 3引入了对CSRF攻击的防御,学习者需要理解如何启用和配置CSRF保护,以及如何处理CSRF令牌。 7. **基于URL的访问控制(URL-Based ...
它支持CSRF(跨站请求伪造)保护,以防御这类常见攻击。Web安全方面,Spring Security可以拦截和处理HTTP请求,进行URL级别的过滤和重定向,例如未授权访问的重定向。 配置Spring Security通常涉及XML或Java配置。...
3. **CSRF 防御**:为敏感操作添加 CSRF 防御,如生成和验证 CSRF 令牌。 4. **HTTP 只能安全**:确保登录请求使用 HTTPS,保证传输过程中的数据安全。 **总结** "springmvc简单的登陆"是一个基础的 Spring MVC ...
2. **HTTP安全**:Spring Security提供了强大的HTTP安全特性,包括CSRF(跨站请求伪造)防护、XSS(跨站脚本攻击)防御、HTTP头部安全设置等,帮助开发者构建更安全的Web应用。 3. **认证与授权**:Spring Security...
- **CSRF 防御**:生成并验证 CSRF 令牌,防止跨站请求伪造攻击。 5. **异常处理**: - 使用 Spring MVC 提供的 @ExceptionHandler 注解或全局异常处理器来捕获并处理异常,提供友好的错误信息。 6. **单元测试*...
5. 防御策略:了解如何防止XSS、CSRF等攻击,以及Session管理的最佳实践。 在学习过程中,结合中文的Spring Security官方文档将有助于你深入理解各项功能和配置。同时,阅读Springside的相关教程,通过实际操作来...
1. **Web应用安全**:Spring Security可以轻松集成到Spring MVC应用中,保护RESTful API或Web页面。 2. **SOA安全**:通过提供DelegatingPasswordEncoder,3.1.0.RELEASE支持在分布式环境中进行密码加密和验证。 3...
4. **启动 Shiro**:在 Spring 初始化时,通过 `ShiroFilter` 注册 Shiro 过滤器,将 Shiro 的拦截逻辑融入到 Spring MVC 的请求处理流程中。 **实例解析** 提供的资源“Shiro和Spring整合完全可运行”应该是一个...
在Spring MVC中,可以使用`@ResponseBody`注解结合`StringHttpMessageConverter`的`setWriteCharset(Charset charset)`方法,确保输出的字符集支持正确的编码。同时,使用`HttpMessageConverter`的子类,如`...
- **CSRF 攻击防护**:跨站请求伪造攻击的防御策略。 #### 八、Spring4 与数据库交互 - **JDBC 支持**:Spring 提供了简化 JDBC 编程的模板,降低了异常处理的复杂性。 - **ORM 支持**:支持 Hibernate、MyBatis ...
4. **Spring MVC的防护**:Spring MVC框架提供了内置的安全防护机制。可以使用`@ResponseBody`注解配合`HttpMessageConverter`进行自动编码,或者使用`@SpringBootApplicaiton`启动类上的`...