CSRF/XSRF 保护 Spring 安全和 AngularJS
CSRF/XSRF protection for Spring Security and AngularJS
我试图为我的应用程序添加 CSRF/XSRF 保护,但 运行 进入 st运行ge 行为。所有 get 请求都工作正常,但在所有 post/put/delete 上,我收到 403 Unauthorized。最重要的是,当我尝试调试我的 CSRF 过滤器时,请求没有到达它,它们在较早的某个地方被拒绝了。他们甚至没有达到我的身份验证过滤器,所以我无法弄清楚可能是什么问题。
我的安全配置:
@Override
public void configure(HttpSecurity http) throws Exception {
http
...
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilterBefore(new StatelessAuthenticationFilter(tokenAuthenticationService()), UsernamePasswordAuthenticationFilter.class)
.addFilterAfter(new CsrfHeaderFilter(), CsrfFilter.class)
.csrf().csrfTokenRepository(csrfTokenRepository());
}
private CsrfTokenRepository csrfTokenRepository() {
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
repository.setHeaderName("X-XSRF-TOKEN");
return repository;
}
我没有添加过滤器,因为正如我所说,请求没有到达它们。但如果需要,我会完成我的问题。希望得到您的帮助,在此先感谢您!
假设您 config/filters 的其余部分工作正常,您会因为以下原因而面临此问题:SessionCreationPolicy.STATELESS
。
您可以深入了解 Spring CsrfFilter
。您会看到它需要记住会话中每个用户的每个 CSRF 令牌的值,并且由于您没有使用会话,因此无法完成。
接下来要做什么 - 完全取决于您。有人说,如果你的应用是无状态的,实际上就不需要 CSRF 保护。 Spring docs 说 CSRF 攻击仍然相关。我认为这真的取决于您的身份验证机制。
例如,您可能还想查看 this nice article。
希望对您有所帮助。
原则上,Spring 中的 CSRF 机制将 CSRF 令牌存储在 HTTP only cookie 中。因为 JavaScript 无法访问仅 HTTP cookie,您需要告诉 spring 禁用仅 HTTP:
.and().csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
然后您可以从 Angular 读取 cookie,并在每个请求中将其添加到 XSRF-TOKEN header。
这是一般情况。我不确定这是否适合您的特殊情况。
非常感谢您的回答,他们确实帮助我找到了解决方案。如果将来有人遇到同样的问题,我想分享我的解决方案。
如答案中所述,我使用 SessionCreationPolicy.STATELESS
并且没有会话,因此我不得不使用 CookieCsrfTokenRepository
和 withHttpOnlyFalse()
来代替 HttpSessionCsrfTokenRepository
以允许 AngularJS 读取 cookie。
因此,我有这样的配置:
@Override
public void configure(HttpSecurity http) throws Exception {
http
...
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilterBefore(new StatelessAuthenticationFilter(tokenAuthenticationService()), UsernamePasswordAuthenticationFilter.class)
.addFilterAfter(new CsrfHeaderFilter(), CsrfFilter.class)
.csrf().csrfTokenRepository(csrfTokenRepository());
}
如果有人对 CsrfHeaderFilter 的外观感兴趣:
public class CsrfHeaderFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
if (csrf != null) {
Cookie cookie = WebUtils.getCookie(request, "XSRF-TOKEN");
String token = csrf.getToken();
if (cookie==null || token!=null && !token.equals(cookie.getValue())) {
cookie = new Cookie("XSRF-TOKEN", token);
cookie.setPath("/");
response.addCookie(cookie);
}
}
filterChain.doFilter(request, response);
}
}
我的第二个问题是 CORS。 AngularJS 文档说:
"The header will not be set for cross-domain requests."
为了解决这个问题,我不得不使用 HTTP 拦截器:
.factory('XsrfInterceptor', function ($cookies) {
return {
request: function (config) {
var headerName = 'X-XSRF-TOKEN';
var cookieName = 'XSRF-TOKEN';
config.headers[headerName] = $cookies.get(cookieName);
return config;
}
};
});
.config(['$httpProvider', function($httpProvider) {
$httpProvider.interceptors.push('XsrfInterceptor');
}]);
希望我的回答对你有用。
我试图为我的应用程序添加 CSRF/XSRF 保护,但 运行 进入 st运行ge 行为。所有 get 请求都工作正常,但在所有 post/put/delete 上,我收到 403 Unauthorized。最重要的是,当我尝试调试我的 CSRF 过滤器时,请求没有到达它,它们在较早的某个地方被拒绝了。他们甚至没有达到我的身份验证过滤器,所以我无法弄清楚可能是什么问题。
我的安全配置:
@Override
public void configure(HttpSecurity http) throws Exception {
http
...
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilterBefore(new StatelessAuthenticationFilter(tokenAuthenticationService()), UsernamePasswordAuthenticationFilter.class)
.addFilterAfter(new CsrfHeaderFilter(), CsrfFilter.class)
.csrf().csrfTokenRepository(csrfTokenRepository());
}
private CsrfTokenRepository csrfTokenRepository() {
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
repository.setHeaderName("X-XSRF-TOKEN");
return repository;
}
我没有添加过滤器,因为正如我所说,请求没有到达它们。但如果需要,我会完成我的问题。希望得到您的帮助,在此先感谢您!
假设您 config/filters 的其余部分工作正常,您会因为以下原因而面临此问题:SessionCreationPolicy.STATELESS
。
您可以深入了解 Spring CsrfFilter
。您会看到它需要记住会话中每个用户的每个 CSRF 令牌的值,并且由于您没有使用会话,因此无法完成。
接下来要做什么 - 完全取决于您。有人说,如果你的应用是无状态的,实际上就不需要 CSRF 保护。 Spring docs 说 CSRF 攻击仍然相关。我认为这真的取决于您的身份验证机制。
例如,您可能还想查看 this nice article。
希望对您有所帮助。
原则上,Spring 中的 CSRF 机制将 CSRF 令牌存储在 HTTP only cookie 中。因为 JavaScript 无法访问仅 HTTP cookie,您需要告诉 spring 禁用仅 HTTP:
.and().csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
然后您可以从 Angular 读取 cookie,并在每个请求中将其添加到 XSRF-TOKEN header。
这是一般情况。我不确定这是否适合您的特殊情况。
非常感谢您的回答,他们确实帮助我找到了解决方案。如果将来有人遇到同样的问题,我想分享我的解决方案。
如答案中所述,我使用 SessionCreationPolicy.STATELESS
并且没有会话,因此我不得不使用 CookieCsrfTokenRepository
和 withHttpOnlyFalse()
来代替 HttpSessionCsrfTokenRepository
以允许 AngularJS 读取 cookie。
因此,我有这样的配置:
@Override
public void configure(HttpSecurity http) throws Exception {
http
...
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilterBefore(new StatelessAuthenticationFilter(tokenAuthenticationService()), UsernamePasswordAuthenticationFilter.class)
.addFilterAfter(new CsrfHeaderFilter(), CsrfFilter.class)
.csrf().csrfTokenRepository(csrfTokenRepository());
}
如果有人对 CsrfHeaderFilter 的外观感兴趣:
public class CsrfHeaderFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
if (csrf != null) {
Cookie cookie = WebUtils.getCookie(request, "XSRF-TOKEN");
String token = csrf.getToken();
if (cookie==null || token!=null && !token.equals(cookie.getValue())) {
cookie = new Cookie("XSRF-TOKEN", token);
cookie.setPath("/");
response.addCookie(cookie);
}
}
filterChain.doFilter(request, response);
}
}
我的第二个问题是 CORS。 AngularJS 文档说:
"The header will not be set for cross-domain requests."
为了解决这个问题,我不得不使用 HTTP 拦截器:
.factory('XsrfInterceptor', function ($cookies) {
return {
request: function (config) {
var headerName = 'X-XSRF-TOKEN';
var cookieName = 'XSRF-TOKEN';
config.headers[headerName] = $cookies.get(cookieName);
return config;
}
};
});
.config(['$httpProvider', function($httpProvider) {
$httpProvider.interceptors.push('XsrfInterceptor');
}]);
希望我的回答对你有用。