Skip to content

Allow to set the cookie domain in class CookieCsrfTokenRepository #4315

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
renannprado opened this issue Apr 28, 2017 · 6 comments · Fixed by #6276
Closed

Allow to set the cookie domain in class CookieCsrfTokenRepository #4315

renannprado opened this issue Apr 28, 2017 · 6 comments · Fixed by #6276
Assignees
Labels
in: web An issue in web modules (web, webmvc) type: enhancement A general enhancement
Milestone

Comments

@renannprado
Copy link

renannprado commented Apr 28, 2017

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());
    }
}
@jh409
Copy link

jh409 commented Jun 22, 2017

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?

@renannprado
Copy link
Author

renannprado commented Jun 23, 2017

@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 : "/";
        }

    }
}

@bvulaj
Copy link

bvulaj commented Jul 26, 2017

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

@georgeplaton7
Copy link

Any updates on this?

@rwinch
Copy link
Member

rwinch commented Dec 11, 2018

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 rwinch added the status: ideal-for-contribution An issue that we actively are looking for someone to help us with label Dec 11, 2018
@dongmyo
Copy link
Contributor

dongmyo commented Dec 12, 2018

@rwinch I'd like to take it

dongmyo added a commit to dongmyo/spring-security-1 that referenced this issue Dec 13, 2018
jzheaux pushed a commit that referenced this issue Dec 13, 2018
@rwinch rwinch added status: duplicate A duplicate of another issue in: web An issue in web modules (web, webmvc) type: enhancement A general enhancement and removed status: ideal-for-contribution An issue that we actively are looking for someone to help us with status: duplicate A duplicate of another issue labels Dec 14, 2018
@rwinch rwinch added this to the 5.2.0.M1 milestone Dec 14, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: web An issue in web modules (web, webmvc) type: enhancement A general enhancement
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants