Same-Site flag for session cookie in Spring Security

Is it possible to set Same-site Cookie flag in Spring Security?

And if not, is it on a roadmap to add support, please? There is already support in some browsers (i.e. Chrome).


Solution 1:

New Tomcat version support SameSite cookies via TomcatContextCustomizer. So you should only customize tomcat CookieProcessor, e.g. for Spring Boot:

@Configuration
public class MvcConfiguration implements WebMvcConfigurer {
    @Bean
    public TomcatContextCustomizer sameSiteCookiesConfig() {
        return context -> {
            final Rfc6265CookieProcessor cookieProcessor = new Rfc6265CookieProcessor();
            cookieProcessor.setSameSiteCookies(SameSiteCookies.NONE.getValue());
            context.setCookieProcessor(cookieProcessor);
        };
    }
}

For SameSiteCookies.NONE be aware, that cookies are also Secure (SSL used), otherwise they couldn't be applied.

By default since Chrome 80 cookies considered as SameSite=Lax!

See SameSite Cookie in Spring Boot and SameSite cookie recipes.


For nginx proxy it could be solved easily in nginx config:

if ($scheme = http) {
    return 301 https://$http_host$request_uri;
}

proxy_cookie_path / "/; secure; SameSite=None";

UPDATE from @madbreaks: proxy_cookie_flags iso proxy_cookie_path

proxy_cookie_flags ~ secure samesite=none;

Solution 2:

Instead of a Filter, In your Authentication Success Handler, you can mention in this way.

@Override
public void onAuthenticationSuccess(
        HttpServletRequest request, HttpServletResponse response,
        Authentication authentication) throws IOException {
    response.setStatus(HttpServletResponse.SC_OK);
    clearAuthenticationAttributes(request);
    addSameSiteCookieAttribute(response);
    handle(request, response);
}

private void addSameSiteCookieAttribute(HttpServletResponse response) {
    Collection<String> headers = response.getHeaders(HttpHeaders.SET_COOKIE);
    boolean firstHeader = true;
    // there can be multiple Set-Cookie attributes
    for (String header : headers) {
        if (firstHeader) {
            response.setHeader(HttpHeaders.SET_COOKIE,
                    String.format("%s; %s", header, "SameSite=Strict"));
            firstHeader = false;
            continue;
        }
        response.addHeader(HttpHeaders.SET_COOKIE,
                String.format("%s; %s", header, "SameSite=Strict"));
    }
}

It was mentioned in one of the answers. Couldn't find the link after I've implemented it.

Solution 3:

You can always set cookie values by yourself in the Java world if you can get an instance of the HttpServletResponse.

Then you can do:

response.setHeader("Set-Cookie", "key=value; HttpOnly; SameSite=strict")

In spring-security you can easily do this with a filter, here is an example:

public class CustomFilter extends GenericFilterBean {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
                       FilterChain chain) throws IOException, ServletException {
        HttpServletResponse resp = (HttpServletResponse) response;
        resp.setHeader("Set-Cookie", "locale=de; HttpOnly; SameSite=strict");
        chain.doFilter(request, response);
    }
}

Add this filter to your SecurityConfig like this:

http.addFilterAfter(new CustomFilter(), BasicAuthenticationFilter.class)

Or via XML:

<http>
    <custom-filter after="BASIC_AUTH_FILTER" ref="myFilter" />
</http>

<beans:bean id="myFilter" class="org.bla.CustomFilter"/>

Solution 4:

All possible solutions here failed for me. Every time I tried a filter or interceptor, the Set-Cookie header had not yet been added. The only way I was able to make this work was by adding Spring Session and adding this bean into one of my @Configuration files:

@Bean
public CookieSerializer cookieSerializer() {
    DefaultCookieSerializer serializer = new DefaultCookieSerializer();
    serializer.setSameSite("none");
    return serializer;
}

Anyway hope this helps someone else in my same situation.