SpringSecurity启用csrf防攻击(解决post提交失败)

概述

使用spring security后,get请求可以正常访问资源,但提交post请求时,会报错,这是因为security默认开启防csrf攻击

csrf攻击

CSRF(Cross-site request forgery)跨站请求伪造,也被称为One Click Attack或者Session Riding,通常缩写为CSRF或XSRF,是一种对网站的恶意利用。尽管听起来像跨站脚本(XSS),但它与XSS非常不同,XSS利用站点内的信任用户,而CSRF则通过伪装成受信任用户的请求来利用受信任的网站。与XSS攻击相比,CSRF攻击往往不大流行(因此对其进行防范的资源也相当稀少)和难以防范,所以被认为比XSS更具危险性。
CSRF是一种依赖web浏览器的、被混淆过的代理人攻击(deputy attack)。

spring security

springSecurity在2.0之后就会 默认开启防csrf攻击

GET,HEAD,TRACE,OPTIONS请求放行,其他请求(如post、delete、put),都需要带上 csrf token,不带tocken,服务器会认为该请求非法,请求就会被拒绝

CsrfFilter

CsrfFilter处理CSRF校验,详见:
https://www.malaoshi.top/show_1EF5zQbtQP3U.html

如何能够正常发post请求

  • 禁用csrf
  • 前台页面获取csrf token,然后发post请求时,将token一并发出
    • HttpSessionCsrfTokenRepository,适用 jsp、Thymeleaf
    • CookieCsrfTokenRepository ,适用前后台分离式开发

禁用csrf

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable();//关闭打开的csrf保护
    }
}

HttpSessionCsrfTokenRepository

在默认情况下,Spring Security加载的是一个HttpSessionCsrfTokenRepository

HttpSessionCsrfTokenRepository 将 CsrfToken 值存储在 HttpSession 中,并指定前端把CsrfToken 值放在名为_csrf的请求参数或名为X-CSRF-TOKEN的请求头字段里(可以调用相应的设置方法来重新设定)。校验时,通过对比HttpSession内存储的CsrfToken值与前端携带的CsrfToken值是否一致,便能断定本次请求是否为CSRF攻击。

jsp获取token

<input type='hidden' name='${_csrf.parameterName}' value='${_csrf.token}'>

应用场景

这种方式适合 jsp、Thymeleaf等,可以从session中取出token

在前后台分离式开发 会出现问题,因为html、js无法从session中取出token

CookieCsrfTokenRepository

Spring Security还提供了另一种方式,即CookieCsrfTokenRepository
CookieCsrfTokenRepository 是一种更加灵活可行的方案,它将 CsrfToken 值存储在用户的cookie内。

减少了服务器HttpSession存储的内存消耗,并且当用cookie存储CsrfToken值时,前端可以用JavaScript读取(需要设置该cookie的httpOnly属性为false),而不需要服务器注入参数,在使用方式上更加灵活。

存储在cookie中是不可以被CSRF利用的,cookie 只有在同域的情况下才能被读取,所以杜绝了第三方站点跨域获取 CsrfToken 值的可能。CSRF攻击本身是不知道cookie内容的,只是利用了当请求自动携带cookie时可以通过身份验证的漏洞。

注意: 服务器对 CsrfToken 值的校验 不是取自 cookie,而是需要前端 手动将CsrfToken值作为参数携带在请求里

关键代码:

http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())

完整代码:

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) //使用cookie
        .and()

        .authorizeRequests()//url权限配置
//                .antMatchers("/index.html").permitAll() // 匹配这些路径可以所有人访问
                .antMatchers("/user/**").hasRole("admin")
                .anyRequest().authenticated()
                .and()
                //login部分
                .formLogin()
                .loginPage("/login.html")//指定登录页面。所有url前面必须写/,否则报错 'xxx' is not a valid forward URL
                .loginProcessingUrl("/login")//security提供的处理登录url,默认是/login
//                .successForwardUrl("/index.html")//登录成功后跳转的页面,需要额外处理才能使用,否则页面会提示错误
                .defaultSuccessUrl("/index.html")//登录成功后重定向的页面
                .failureUrl("/login_fails.html")//登录失败跳转的页面
//                .failureForwardUrl("/login_fails.html")//登录失败不会转发
                .permitAll()//让login部分的url可以任意访问
//
//                //logout部分
                .and()
                .logout()
                .logoutSuccessUrl("/login.html")//退出成功访问的页面
                .invalidateHttpSession(true)//清空session
                .permitAll();//让logout部分的url可以任意访问
    }
}

访问 http://localhost:8080/ ,在cookie中可以看到 xsrf-token:

修改登录页面

由于登录要发送post请求,所以必须带上token,否则会被过滤器拦截,无法正常提交

从cookie中获取token,并放入表单隐藏域中,一并发出

<script src="/js/js.cookie.min.js"></script>
<form method="post" action="/doLogin">
    用户名:<input name="username"><br>
    密码:<input name="password"><br>
    <input id="_csrf" type="" name="_csrf" value="">
    <input type="submit">
</form>
<script>
    alert(Cookies.get('XSRF-TOKEN'));
    document.getElementById("_csrf").value=Cookies.get('XSRF-TOKEN');
</script>

在表单中添加隐藏域<input id="_csrf" type="" name="_csrf" value="">,提交表单时连带token一起提交

感谢:
https://www.cnblogs.com/dalianpai/p/12393133.html


原文出处:/show_1EF5zSj6yS7R.html