How to set the SameSite attribute in Java Web applications

This short article describes how you can set the SameSite property in HTTP Cookies for Web applications, with special focus on WildFly‘s Web server, which is Undertow.

What is SameSite ? SameSite is a property that can be set in HTTP cookies to avoid false cross-site request (CSRF) attacks in web applications:

  • When SameSite is set to “LAX“, the cookie is sent in requests within the same site and in Get requests from other sites. It is not sent in GET requests that are cross-domain.
  • When SameSite is set to “Strict” it ensures that the cookie is sent in requests only within the same site.
  • When SameSite is set to “None” you enable cookies for cross-site access.

SameSite in Java applications

The Servlet API has not implemented SameSite and so not possible to set it either via code in Java based frameworks or via standard configuration files. There are however several ways to set the SameSite attribute in Undertow Web server. The recommended option, if you are running WildFly 19.0.1 or newer is to add an Undertow handler to your application configuration. Let’s see how:

Create a file named “src/main/webapp/WEB-INF/undertow-handlers.conf” in your Web application with the following content:

path(/webapp)->samesite-cookie(`None`)

This is an Handler predicate which applies the SameSite=None attribute to all cookies for requests under the ‘/webapp’ path.

The SameSite=None attribute, however, is not supported by all clients. In order to skip the attribute check (when the client is not compatible) you can use:

path(/webapp)->samesite-cookie(mode=None, enable-client-checker=false)

Secure SameSite

Although the SameSite attribute is widely supported, it has not been adopted by all developers. The common default of sending cookies everywhere means all use cases work but leaves the user vulnerable to CSRF and unintentional information leakage. To provide users with a safer navigation experience, the IETF proposal (Incrementally Better Cookies lays out two key changes:

  • Cookies without a SameSite attribute will be treated as SameSite=Lax.
  • Cookies with SameSite=None must also specify Secure, meaning they require a secure context.

In terms of Handler configuration, the Secure attribute is done automatically by the handler unless you add “add-secure-for-none=false” parameter in the handler:

path(/webapp)->samesite-cookie(mode=None, enable-client-checker=false,add-secure-for-none=false)

Setting SameSite for older WildFly versions

If you are using a WildFly version older than 19, one simple solution is to add a session-cookie element with the SameSite policy in your Servlet Container configuration:

/subsystem=undertow/servlet-container=default/setting=session-cookie:add(comment="; SameSite=None")

This relects in the following XML configuration:

<servlet-container name="default">
    <jsp-config/>
    <session-cookie comment="; SameSite=None"/>
    <websockets/>
</servlet-container>

Warning: Since Undertow quotes the comment values when using version 0 or version 1 cookies, you need to set the System Property io.undertow.cookie.DEFAULT_ENABLE_RFC6265_COOKIE_VALIDATION to true at start up::

 ./bin/standalone.sh -Dio.undertow.cookie.DEFAULT_ENABLE_RFC6265_COOKIE_VALIDATIOM

Setting SameSite with a Servlet Filter

On the other hand, if you are not using WildFly as application server, the recommended approach, until this is supported in Jakarta Servlet specification, is to use a simple Servlet Filter to inject the SameSite attribute in the HTTP Response:

import java.io.IOException;
import java.util.Collection;

import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;

public class SameSiteFilter implements javax.servlet.Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        chain.doFilter(request, response);
        addSameSiteAttribute((HttpServletResponse) response); // add SameSite=strict cookie attribute
    }

    private void addSameSiteAttribute(HttpServletResponse response) {
        Collection<String> headers = response.getHeaders("Set-Cookie");
        boolean firstHeader = true;
        for (String header : headers) {  
            if (firstHeader) {
                response.setHeader("Set-Cookie", String.format("%s; %s", header, "SameSite=Strict"));
                firstHeader = false;
                continue;
            }
            response.addHeader("Set-Cookie", String.format("%s; %s", header, "SameSite=Strict"));
        }
    }

    @Override
    public void destroy() {

    }
}

Setting SameSite in the httpd front end

Finally, if your application server is fronted by an httpd server, you can also set the SameSite attribute using the Header directive.

For example, to set SameSite only on JSESSIONID cookie:

Header edit Set-Cookie ^(JSESSIONID.*)$ $1;HttpOnly;Secure;SameSite=None

To set SameSite on ALL cookies :

Header edit Set-Cookie ^(.*)$ $1;HttpOnly;Secure;SameSite=None