Search code examples
angularjsspring-bootcsrfcsrf-protection

Spring Boot and CSRF with AngularJS - Forbitten 403 -> wrong logout


In my Spring Boot/AngularJS application I have the following CSRF- Configuration:

@Override
protected void configure(final HttpSecurity http) throws Exception {
    http.csrf().csrfTokenRepository(csrfTokenRepository());    http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

    final String[] restEndpointsToSecure = WebSecurityConfig.restEndpointsToSecure;
    for (final String endpoint : restEndpointsToSecure) {
        http.authorizeRequests().antMatchers("/" + endpoint + "/**").hasRole(UserRoleEnum.USER.toString());
    }

    http.addFilterAfter(csrfTokenResponseHeaderBindingFilter(), CsrfFilter.class);

    xAuthTokenConfigurer.setDetailsService(userDetailsServiceBean());
    final SecurityConfigurer<DefaultSecurityFilterChain, HttpSecurity> securityConfigurerAdapter = xAuthTokenConfigurer;
    http.apply(securityConfigurerAdapter);
}

The CSRF- Token Filter looks like this:

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, javax.servlet.FilterChain filterChain)
        throws ServletException, IOException {

    final CsrfToken csrf = (CsrfToken)request.getAttribute(CsrfToken.class.getName());
    if (csrf != null) {
        Cookie cookie = WebUtils.getCookie(request, "XSRF-TOKEN");
        final 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);
}

In general it works fine, the Request header property X-XSRF-TOKEN is send with each request. BUT I have a strange behaviour. I will update my user- profile in application. The first time it works fine, the second one, I get an HTTP 403 Forbidden and actually I really don't know why. I do nothing between this two updates (no navigation to other pages between this two updates or something else).

At the picture at the bottom the left Request is the one which works and the right one failes. The only different is that at the right one the property Set-Cookie and X-Application-context is missing in Response header. The request headers are equal.

Does anyone know what I am doing wrong here. Its kind of mystic here for me.

enter image description here


Solution

  • It seems as though there might be some functional error in your front-end userProfile.controller

    Your Cookie SessionID redirected form the request header is getting same in the response header which results in INVALID_SESSIONID hence the user is redirected to the login page or your signup page

    As a Solution to this you can either correct your frontend controller or as another workaround for this you can add the following to your code

    • Add the CORS headers only if the CORS is detected and pass the origin header so that to allow access to the user content.
    • Respond to the OPTIONS request with a http 200 message

       static final String ORIGIN = "Origin";
      
      if (request.getHeader(ORIGIN).equals("null")) {
              String origin = request.getHeader(ORIGIN);
              response.setHeader("Access-Control-Allow-Origin", "*");//* or origin as u prefer
              response.setHeader("Access-Control-Allow-Credentials", "true");
             response.setHeader("Access-Control-Allow-Headers",
                      request.getHeader("Access-Control-Request-Headers"));
          }
          if (request.getMethod().equals("OPTIONS")) {
              try {
                  response.getWriter().print("OK");
                  response.getWriter().flush();
              } catch (IOException e) {
                  e.printStackTrace();
              }
          }
      

      Then add the custom filter to be invoked

            //your other configs
            < security:custom-filter ref="corsHandler" after="PRE_AUTH_FILTER"/>
      

    Full attribution goes to this thread answer by the author