Spring Security和AngularJS的CSRF / XSRF保护

时间:2017-09-09 09:26:35

标签: angularjs spring spring-security csrf x-xsrf-token

我尝试将CS​​RF / XSRF保护添加到我的应用程序中,但遇到了奇怪的行为。所有获取请求都可以正常工作,但在所有发布/放置/删除时我都获得了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;
        }

我没有添加过滤器,因为正如我所说,请求没有到达它们。但如果需要,我会完成我的问题。我希望得到你的帮助,提前谢谢你!

3 个答案:

答案 0 :(得分:0)

假设您的配置/过滤器的其余部分正常运行,您就会因此而遇到此问题:SessionCreationPolicy.STATELESS

你可以看看Spring CsrfFilter的内幕。您将看到它需要记住会话中每个用户的每个CSRF令牌的值,并且由于您没有使用会话,因此无法完成。

下一步该做什么 - 真的取决于你。有人说,如果你的应用程序是无状态的,实际上不需要CSRF保护。 Spring docs说CSRF攻击仍然具有相关性。我认为这实际上取决于您的身份验证机制。

您可能还想查看this nice article,例如。

希望它有所帮助。

答案 1 :(得分:0)

原则上,Spring中的CSRF机制将CSRF令牌存储在仅HTTP的cookie中。由于JavaScript无法访问仅HTTP的cookie,因此您需要告诉spring仅禁用HTTP:

.and().csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());

然后,您可以从Angular中读取cookie,并将每个请求添加到XSRF-TOKEN标头中。

这是一般情况。我不确定这是否适合您的特殊情况。

答案 2 :(得分:0)

非常感谢答案,他们真的帮助我找到了解决方案。如果将来有人会面临同样的问题,我想分享我的解决方案。

正如答案中所述,我使用了SessionCreationPolicy.STATELESS并且没有会话,因此我不得不使用HttpSessionCsrfTokenRepository CookieCsrfTokenRepository而不是withHttpOnlyFalse()来允许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文档说:

  

"不会为跨域请求设置标头。"

要解决这个问题,我必须使用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');
}]);

我希望我的答案有用。