CSRF令牌未被接受(春季)

时间:2016-02-08 10:56:36

标签: spring csrf

将CSRF添加到现有和正在运行的CORS配置时遇到问题。

每次触发POST,PUT或DELETE时都会收到我当前令牌不正确的错误(找到无效的CSRF令牌' edff86dc-093a-4df9-8218-e5343506bdf9'请求参数' _csrf'或标题' X-CSRF-TOKEN'。)。

但是当我比较它们时,它不会由令牌引起。此外,如果我在此之后触发GET(例如PUT),则再次发送令牌并接受。

所以我假设我的安全配置可能有问题,但我不知道我错过了什么。

安全配置:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.addFilterBefore(new CorsFilter(), ChannelProcessingFilter.class);
    http.authorizeRequests()
    .antMatchers(HttpMethod.OPTIONS, "/*/**").permitAll()
    .antMatchers("/logout", "/admin/**").authenticated();
    http.csrf().ignoringAntMatchers("/guestbook/**");
    http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint);
    http.formLogin().successHandler(authenticationSuccessHandler);
    http.logout().logoutSuccessHandler(logoutSuccessHandler);
    http.addFilterAfter(new CsrfTokenResponseHeaderBindingFilter(), CsrfFilter.class);
}

令牌过滤器:

public class CsrfTokenResponseHeaderBindingFilter extends OncePerRequestFilter {
protected static final String REQUEST_ATTRIBUTE_NAME = "_csrf";
protected static final String RESPONSE_HEADER_NAME = "X-CSRF-HEADER";
protected static final String RESPONSE_PARAM_NAME = "X-CSRF-PARAM";
protected static final String RESPONSE_TOKEN_NAME = "X-CSRF-TOKEN";

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, javax.servlet.FilterChain filterChain) throws ServletException, IOException {
    CsrfToken token = (CsrfToken) request.getAttribute(REQUEST_ATTRIBUTE_NAME);
    System.out.println(token.getToken());
    if (token != null) {
        response.setHeader(RESPONSE_HEADER_NAME, token.getHeaderName());
        response.setHeader(RESPONSE_PARAM_NAME, token.getParameterName());
        response.setHeader(RESPONSE_TOKEN_NAME , token.getToken());
    }

    filterChain.doFilter(request, response);
}
  }

,例如cors过滤器:

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
    HttpServletResponse response = (HttpServletResponse) res;
    HttpServletRequest request = (HttpServletRequest) req;
    String origin = request.getHeader("Origin");

    response.addHeader("Access-Control-Allow-Origin", origin);
    response.setHeader("Access-Control-Allow-Credentials", "true");
    response.addHeader("Access-Control-Max-Age", "10");
    response.addHeader("Access-Control-Expose-Headers", "X-CSRF-TOKEN");
    response.addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");

    String headers = request.getHeader("Access-Control-Request-Headers");

    if (!StringUtils.isEmpty(headers )) {
        response.addHeader("Access-Control-Allow-Headers", headers );
    }

    if (request.getMethod().equals("OPTIONS")) {

        try {
            response.getWriter().print("OK");
            response.getWriter().flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    } else{
        chain.doFilter(request, response);
    }
}

问题不仅在我登录时发生。如果我不会在留言簿路径上禁用csrf,那么也可能没有POST。

我希望任何人都可以给我一个提示。

问候

1 个答案:

答案 0 :(得分:0)

所以我终于解决了我的问题。经过大量的搜索和错误后,我发现了CsrfProtectionMatcher,它可用于在不同的路径上启用CSRF。

无论如何,这对我来说非常困惑,因为我认为默认情况下每次请求都会始终启用CSRF。因此,只要我在“/ admin”路径上应用CsrfProtectionMatcher(允许所有可能的方法,指定为null)就可以了。 requireCsrfProtectionMatcher on docs.spring.iodetailed article

此外,我现在可以使用更简单的配置,但尽管我的旧配置也可以。

旧的CsrfProtectionMatcher:

case $OPTION in
  # the following matches
  #  - single-digit numbers 0-9
  #  - two-digit numbers starting with either 1 or 2
  #  - the number 30
  [0-9]|[12][0-9]|30)
     IMAGE=${options[$(($OPTION-1))]%.tar}
     ;;
  *)
     echo "invalid option" 1>&2
     exit 1
esac

更简单的配置:

protected void configure(HttpSecurity http) throws Exception {
    RequestMatcher csrfRequestMatcher = new RequestMatcher() {

        private RegexRequestMatcher requestMatcher =
                new RegexRequestMatcher("/admin", null);

        public boolean matches(HttpServletRequest request) {

            if(requestMatcher.matches(request))
                return true;

            return false;
        }

    };      

    http.addFilterBefore(new CorsFilter(), ChannelProcessingFilter.class);
    http
    .csrf()
        .requireCsrfProtectionMatcher(csrfRequestMatcher);
    http.authorizeRequests()
    .antMatchers(HttpMethod.OPTIONS, "/*/**").permitAll()
    .antMatchers("/login", "/**/**/**").permitAll()
    .antMatchers("/logout", "/admin/**").authenticated();
    http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint);
    http.formLogin().successHandler(authenticationSuccessHandler);
    http.logout().logoutSuccessHandler(logoutSuccessHandler);
    http.addFilterAfter(new CsrfTokenResponseHeaderBindingFilter(), CsrfFilter.class);
}

我不得不承认我仍然不知道为什么必须明确启用CSRF。如果有人对此有答案,请告诉我。