CSRF 详解:攻击,防御,Spring Security应用等( 三 )


CSRF 防御实战主流的框架一般都包含了CSRF的拦截 。
非框架型 - 自定义XXXCsrfFilter可以通过自定义xxxCsrfFilter去拦截实现,这里建议你参考 Spring Security - org.springframework.security.web.csrf.CsrfFilter.java 。
Spring Security - 什么时候禁用CSRF你开发的应用在何时,会考虑禁用CSRF呢? 这时候需要考虑CSRF本质是盗用cookie, 无cookie方案就可以禁用 。

  • 如果你只是创建一个非浏览器客户端使用的服务,你可能会想要禁用CSRF保护
Spring Security中禁用CSRF@EnableWebSecuritypublic class WebSecurityConfig extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) throws Exception {http.csrf().disable();// 默认是启用的,需要禁用CSRF保护}}Spring Security - CookieCsrfTokenRepository.withHttpOnlyFalse()存Cookie,比如前后端分离方案:Spring Security CookieCsrfTokenRepository + 前端路由统一设置
Spring Security依赖包<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency>Spring Security - CookieCsrfTokenRepository.withHttpOnlyFalse()@Overrideprotected void configure(HttpSecurity http) throws Exception {// 本例子给个范例而已,对于xxx的部分,自己根据业务定义http.authorizeRequests()/* allow */.antMatchers("/plugins/**", "/api-docs/**") .permitAll().antMatchers("/login", "/logout").permitAll()/* auth control */.antMatchers("/xxx/user", "/xxx/user/**").access("hasAuthority('xxx:user')").antMatchers("/xxx/role", "/xxx/role/**").access("hasAuthority('xxx:role')")/* others */.anyRequest().authenticated()/* other Filters */.and().addFilterBefore(xxxFilter(), UsernamePasswordAuthenticationFilter.class)/* iframe */.headers().frameOptions().sameOrigin()/* form login & logout */.and().formLogin().loginPage("/login").usernameParameter("username").passwordParameter("password").defaultSuccessUrl("/admin/", true).and().rememberMe().rememberMeParameter("remember").rememberMeCookieName("remember").and().logout().deleteCookies("JSESSIONID").invalidateHttpSession(true).logoutSuccessHandler(new XXXLogoutSuccessHandler(localeResolver())).logoutRequestMatcher(new AntPathRequestMatcher("/logout")).permitAll()/* csrf */.and().csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());//.and().cors()}后端thymeleaf登录页面"/login":<!DOCTYPE html><html lang="en" xmlns:th="http://www.thymeleaf.org"><head><meta charset="UTF-8"><title>登录页面</title></head><body><form id="form" method="post"><label>用户名:</label><input name="username" type="text" value=https://www.isolves.com/it/aq/wl/2020-01-08/"" />
前端调用后端API: 方式一 (前后端分离的)://将Cookie转换为JS Objectfunction initCookies() {var cookie = document.cookie,items = cookie.split(";"),keys = {};items.forEach(function(item) {var kv = item.split('=');keys[$.trim(kv[0])] = $.trim(kv[1]);});return keys;}//提交数据$.post(url, {userId : code,_csrf : initCookies()['X-XSRF-TOKEN'];}, function(datas) {//TODO something})前端调用后端API: 方式二 (后端写前端,用的后端模板) :<meta name="_csrf" content="${_csrf.token}"/><meta name="_csrf_header" content="${_csrf.headerName}"/> <script>var token = $("meta[name='_csrf']").attr("content");var header = $("meta[name='_csrf_header']").attr("content");$.ajaxSetup({beforeSend: function (xhr) {if(header && token ){xhr.setRequestHeader(header, token);}}});</script>Spring Security - new CookieCsrfTokenRepository()可以通过 new CookieCsrfTokenRepository() 自定义拦截的逻辑,大概意思:
@Configurationpublic class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) throws Exception {http.csrf().csrfTokenRepository(new CookieCsrfTokenRepository()).requireCsrfProtectionMatcher(/*** 拦截“/login”开头的访问路径,不让访问* 拦截所有“POST”请求,不让访问*///httpServletRequest -> httpServletRequest.getRequestURI().startsWith("/login")//&& httpServletRequest.getMethod().equals("POST")httpServletRequest -> httpServletRequest.getMethod().equals("POST"));}}


推荐阅读