【问题标题】:CSRF/XSRF protection for Spring Security and AngularJS用于 Spring Security 和 AngularJS 的 CSRF/XSRF 保护
【发布时间】:2018-02-18 03:05:39
【问题描述】:

我尝试向我的应用程序添加 CSRF/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;
        }

我没有添加过滤器,因为正如我所说,请求没有到达它们。但如果需要,我会完成我的问题。希望对您有所帮助,在此先感谢您!

【问题讨论】:

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


    【解决方案1】:

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

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

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

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

    【讨论】:

      【解决方案2】:

      假设您的配置/过滤器的其余部分正常工作,您将因此面临这个问题:SessionCreationPolicy.STATELESS

      您可以了解 Spring CsrfFilter 的幕后花絮。您会看到它需要记住会话中每个用户的每个 CSRF-token 的值,并且由于您没有使用会话,因此无法完成。

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

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

      希望对你有帮助。

      【讨论】:

      • 出于这个原因,我实现了我的自定义 CSRF 过滤器,但我的问题是请求甚至没有到达它。这很奇怪。
      • @AnarSultanov,你能分享你过滤器的代码吗?不过,这并不重要,因为您在 Spring 的 CsrfFilter 之后添加了过滤器。如果 Springs 过滤器无法匹配来自用户会话的令牌(因为没有会话而无法获取),它会调用accessDeniedHandler 并执行return(破坏过滤器链),这意味着它没有调用filterChain.doFilter(request, response);,所以你的过滤器实际上不能被调用。如果您正在实施自己的 CSRF 保护 - 那么,我想,您应该通过调用 .csrf().disable() 来禁用 Spring 的实施。
      • 我发现了我的问题。我在 cookie 中有 XSRF-TOKEN,但是 Angular 不会将带有此令牌的标头添加到请求中。现在我正试图找出原因以及如何解决这个问题。
      【解决方案3】:

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

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

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

      为了解决这个问题,我不得不使用 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');
      }]);
      

      希望我的回答对你有用。

      【讨论】:

        猜你喜欢
        • 2016-01-12
        • 2016-02-10
        • 2022-11-17
        • 2016-09-01
        • 2015-09-13
        • 2014-12-17
        • 2014-02-09
        • 2014-03-14
        相关资源
        最近更新 更多