为了防止SpringSecurity跨域请求伪造(CSRF)攻击,需要采取一些措施来进行防护实现。下面是实现CSRF防护的步骤:
1.同源检查
这是最常见的CSRF防护方法,包括验证请求的源(Origin),或者Referrer)与app地址是否相同,建议把这个配置在Spring Security中,只需在SpringSecurity的配置类中添加如下代码:
http.csrf().requireCsrfProtectionMatcher(new CSRFRequireSamesiteMatcher());
此处使用CSRFRequireSamesiteMatcher
来进行same-site检测,这可以防止第三方站点进行CSRF攻击。如果源(Origin),或者Referrer)与app地址不同,spring security将拒绝该请求。
2.Token验证
在服务器端为每个用户生成一个随机的token,然后将其存储在session中,并在每个表单提交中包含一个隐藏的字段,包含Token。客户端使用该token作为表单参数,服务端进行校验。SpringSecurity已经集成了处理该场景的组件CsrfTokenRepository
,你可以直接注入此组件并使用。 这个过程需要两个步骤:
2.1 将Token插入到所有表单
Springsecurity在JSP端预置了名为_csrf的Token字段,你只需要在jps的form中使用即可:
<form action="/login" method="post">
<input type="text" name="username" />
<input type="password" name="password" />
<input type="submit" value="Login"/>
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
</form>
在默认情况下,Spring Security的所有EndPoint或者controller都能够解析名为_csrf的token字段。
2.2 创建Token工厂和存储库
创建跨站点请求伪造保护所需的csrfTokenRepository Bean,详情请参考以下示例:
http.csrf()
.csrfTokenRepository(csrfTokenRepository());
private CsrfTokenRepository csrfTokenRepository() {
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
repository.setSessionAttributeName("_csrf");
return repository;
}
此处使用了HttpSessionCsrfTokenRepository
,用于将生成的token存储在当前用户的session中,如果Oauth2集成的话,使用的是OAuth2AuthorizedClientCsrfTokenRepository
以上便是Spring Security防护CSRF攻击的两种常见的方法。
示例1:
在Spring Security中使用CsrfToken进行跨站请求伪造保护:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/css/**", "/index").permitAll().antMatchers("/user/**")
.hasRole("USER").and().formLogin().loginPage("/login").failureUrl("/login-error")
.and().exceptionHandling().accessDeniedPage("/401");
http.csrf().csrfTokenRepository(csrfTokenRepository());
}
private CsrfTokenRepository csrfTokenRepository() {
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
repository.setSessionAttributeName("_csrf");
return repository;
}
}
在JSP的表单中,将_csrf的Token属性作为隐藏的字段:
<form th:action="@{/logout}" method="post">
<input type="submit" value="Log out" />
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
</form>
示例2:
在Spring Security中使用SameSite来防范跨站请求伪造攻击。
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/css/**", "/index").permitAll().antMatchers("/user/**")
.hasRole("USER").and().formLogin().loginPage("/login").failureUrl("/login-error")
.and().exceptionHandling().accessDeniedPage("/401");
http.csrf().requireCsrfProtectionMatcher(new CSRFRequireSamesiteMatcher());
}
}
public class CSRFRequireSamesiteMatcher implements RequestMatcher {
private static final Pattern ALLOWED_METHODS = Pattern.compile("^(GET|HEAD|TRACE|OPTIONS)$");
private static final Pattern API_PATTERN = Pattern.compile("^/api(?!/auth).*$");
private static final String STRICT = "Strict";
@Override
public boolean matches(HttpServletRequest request) {
String path = request.getRequestURI();
if (ALLOWED_METHODS.matcher(request.getMethod()).matches() || API_PATTERN.matcher(path).matches()) {
return false;
}
String samesite = request.getHeader("Cookie");
return samesite != null && !containsToken(samesite, STRICT);
}
private boolean containsToken(String samesite, String token) {
String[] tokens = samesite.split(";");
for (String t : tokens) {
if (t.trim().startsWith(token)) {
return true;
}
}
return false;
}
}
以上即为Spring Security中防护CSRF的常见方法。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:SpringSecurity跨域请求伪造(CSRF)的防护实现 - Python技术站