Spring-security: Allow to set the cookie domain in class CookieCsrfTokenRepository

Created on 29 Apr 2017  路  6Comments  路  Source: spring-projects/spring-security

Summary

We need to set the domain of the CSRF cookie, because we have many subdomains accessing the our API under api.example.com. With the current implementation the cookie is only accessible from api.example.com, but the users will never go to this domain. They will go to x.example.com, example.com, etc...

Actual Behavior

No method to set the cookie domain.

Expected Behavior

Provided method to customize the cookie domain

Version

4.2.2.RELEASE

Sample

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf()
            .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
    }
}
web enhancement

Most helpful comment

Any thoughts on this from the spring team? This would be useful to us as well.

All 6 comments

We're experiencing the same problem.

We have api.example.com with web.example.com as the front-end, in Angular2. The Set-Cookie comes back from Spring but, being a different domain, the cookie is lost.

@renannprado , did you find another way to approach this?

@jh409 I'm no longer using this, 'cause we've changed the XSRF protection we're using.
However I guess this should help you out.
Disclaimer: I had to do some copy/paste from the original implementation 'cause there was no other way to do what I wanted.

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.CsrfConfigurer;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
import org.springframework.security.web.csrf.CsrfToken;
import org.springframework.security.web.csrf.CsrfTokenRepository;
import org.springframework.util.StringUtils;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;

/**
 * TODO: change the implementation when https://github.com/spring-projects/spring-security/issues/4315 is fixed
 */
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Value("${xx.csrf.cookie.domain}")
    private String CSRF_COOKIE_DOMAIN;
    @Value("${xx.csrf.cookie.name}")
    private String CSRF_COOKIE_NAME;

    @Autowired
    private Environment environment;

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        final CsrfConfigurer<HttpSecurity> httpSecurityCsrfConfigurer = http.csrf()
                .csrfTokenRepository(new CustomDomainCookieCsrfTokenRepository(CSRF_COOKIE_DOMAIN, CSRF_COOKIE_NAME))
                // the method to verify if the links are saved won't require CSRF protection
                .ignoringAntMatchers("/**/xx/yy/*");

        // will let the CSRF protection enabled unless we have the disable-csrf profile active
        if (Arrays.stream(environment.getActiveProfiles()).anyMatch(profile -> profile.equals("disable-csrf"))) {
            httpSecurityCsrfConfigurer.disable();
        }
    }

    /**
     * saveToken method had to be coped from {@link CookieCsrfTokenRepository} because the class is final
     */
    public static class CustomDomainCookieCsrfTokenRepository implements CsrfTokenRepository {
        private final CookieCsrfTokenRepository cookieCsrfTokenRepository;
        private final String cookieDomain;
        private final String cookieName;

        public CustomDomainCookieCsrfTokenRepository(final String cookieDomain, final String cookieName) {
            this.cookieCsrfTokenRepository = CookieCsrfTokenRepository.withHttpOnlyFalse();
            this.cookieCsrfTokenRepository.setCookieName(cookieName);
            this.cookieDomain = cookieDomain;
            this.cookieName = cookieName;
        }

        @Override
        public CsrfToken generateToken(final HttpServletRequest request) {
            return cookieCsrfTokenRepository.generateToken(request);
        }

        @Override
        public void saveToken(final CsrfToken token, final HttpServletRequest request, final HttpServletResponse response) {
            String tokenValue = token == null ? "" : token.getToken();
            Cookie cookie = new Cookie(cookieName, tokenValue);
            cookie.setSecure(request.isSecure());
            cookie.setDomain(cookieDomain);
            if (!StringUtils.isEmpty(this.cookieCsrfTokenRepository.getCookiePath())) {
                cookie.setPath(this.cookieCsrfTokenRepository.getCookiePath());
            } else {
                cookie.setPath(this.getRequestContext(request));
            }
            if (token == null) {
                cookie.setMaxAge(0);
            }
            else {
                cookie.setMaxAge(-1);
            }
//            we don't need this because our cookie won't be http only
//            if (cookieHttpOnly && setHttpOnlyMethod != null) {
//                ReflectionUtils.invokeMethod(setHttpOnlyMethod, cookie, Boolean.TRUE);
//            }

            response.addCookie(cookie);
        }

        @Override
        public CsrfToken loadToken(final HttpServletRequest request) {
            return cookieCsrfTokenRepository.loadToken(request);
        }

        private String getRequestContext(HttpServletRequest request) {
            String contextPath = request.getContextPath();
            return contextPath.length() > 0 ? contextPath : "/";
        }

    }
}

Any thoughts on this from the spring team? This would be useful to us as well.

Any updates on this?

We don't have any plans for implementing this ourselves a the moment. However, if someone wants to provide a PR that would be very welcome.

@rwinch I'd like to take it

Was this page helpful?
0 / 5 - 0 ratings