Spring-Security对CSRF攻击的支持
何时使用CSRF保护
什么时候应该使用CSRF保护?我们的建议是使用CSRF保护,可以通过浏览器处理普通用户的任何请求。如果你只是创建一个非浏览器客户端使用的服务,你可能会想要禁用CSRF保护。(即所有处理来自浏览器的请求需要是CSRF保护,如果后台服务是提供API调用那么可能就要禁用CSRF保护)
CSRF保护默认情况下使用Java配置启用
@EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .csrf().disable();//禁用CSRF保护 } } |
SpringBoot实例中应用CSRF
前提注意:
The URL that triggers log out to occur (default is |
CSRF在SpringSecurity中默认是启动的,那么你的退出请求必须改为POST请求。这确保了注销需要CSRF令牌和一个恶意的用户不能强制注销用户
所以在SpringSecurity中需要重新配置登出
package com.niugang.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.WebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; @Configuration // 里面已经包含了@Component 所以不用再上下文中在引入入了 @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { // spring自带的 @Autowired private UserDetailsService userDetailsService; /** * configure(HttpSecurity)方法定义了哪些URL路径应该被保护 */ @Override protected void configure(HttpSecurity http) throws Exception { * 忽略静态资源 */ @Override public void configure(WebSecurity web) throws Exception { /* * 在springboot中忽略静态文件路径,直接写静态文件的文件夹 springboot默认有静态文件的放置路径,如果应用spring * security,配置忽略路径 不应该从springboot默认的静态文件开始 * 如:在本项目中,所有的js和css都放在static下,如果配置忽略路径,则不能以static开始 * 配置成web.ignoring().antMatchers("/static/*");这样是不起作用的 */ web.ignoring().antMatchers("/themes/**", "/script/**"); } /** * 配置自定义用户服务 */ @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService); // .passwordEncoder(passwordEncoder()); } /** * 密码加密 */ /* * @Bean public BCryptPasswordEncoder passwordEncoder() { return new * BCryptPasswordEncoder(); } */ } |
HTML中需要以表单形式POST提交退出 <form action="logout" method="post"> |
在登录页面输入用户名和密码,点击登录,页面报如下错误
这就是页面在登录时没有向后台传入后台颁发的令牌,具体代码如下:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> <link href="themes/bootstrap.min.css" rel="stylesheet" /> <script type="text/javascript" src="script/jquery.min.js"></script> <!--layer弹出框--> <link rel="stylesheet" href="script/layer/mobile/need/layer.css"> <script type="text/javascript" src="script/layer/layer.js"></script> </head> <!-- --> <style> form { width: 60%; margin: 0 auto; } </style> <body> <form action="checkLogin" method='post'> <h2 style="text-align: center">spring boot</h2> <div class="form-group"> <label for="name">姓名</label> <input type="text" class="form-control" name="username" placeholder="姓名"> </div> <div class="form-group"> <label for="password">密码</label> <input type="password" class="form-control" name="password" placeholder="密码"> </div> <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" /> <button type="submit" id="btn_save" class="btn btn-primary form-control">保存</button> </form> <script type="text/javascript"> <#if errorMessage??> layer.msg('${errorMessage}', {icon: 4,time:1000,anim: 6}); </#if> </script> </body> </html> |
这样才能登录成功
对于Ajax和JSON的异步请求
<head> <meta charset="UTF-8"> <meta name="_csrf" content="${_csrf.token}" /> <!-- default header name is X-CSRF-TOKEN --> <meta name="_csrf_header" content="${_csrf.headerName}" /> <title>Insert title here</title> <link rel="stylesheet" type="text/css" href="themes/bootstrap.min.css" /> <script type="text/javascript" src="script/jquery.min.js"></script> <script type="text/javascript" src="script/bootstrap.js"></script> <!--layer弹出框--> <link rel="stylesheet" href="script/layer/mobile/need/layer.css"> <script type="text/javascript" src="script/layer/layer.js"></script> <style type="text/css"> button { margin-left: 15px; } </style> </head> |
var token = $("meta[name='_csrf']").attr("content"); var header = $("meta[name='_csrf_header']").attr("content"); $(document).ajaxSend(function(e, xhr, options) { xhr.setRequestHeader(header, token); }); |
用户想要坚持CSRF Token在cookie中。 默认情况下CookieCsrfTokenRepository将编写一个名为 XSRF-TOKEN的cookie和从头部命名 X-XSRF-TOKEN中读取或HTTP参数 _csrf。
代码如下: .and().csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) |
示例显式地设置cookieHttpOnly=false. 这是必要的,允许JavaScript(例如AngularJS)读取它。 如果你不需要使用JavaScript直接读取cookie的能力,建议省略 cookieHttpOnly=false (通过使用new CookieCsrfTokenRepository()代替) 提高安全性.
The URL that triggers log out to occur (default is |