Reputation: 2386
I'm trying to disable the creation of the JSESSIONID
Cookie conditionally.
I only want to create this cookie if a certain cookie in the request is present.
I'm using spring-boot-starter-web
2.5.3
with spring-boot-starter-security
.
I tried to add spring-session
and define a custom DefaultCookieSerializer
-Bean. It turned out that the JSESSIONID
is not actually coming from spring, but from the underlying Tomcat.
javax.servlet.Filter
which blocks setting the Set-Cookie
Header:package xxxx.configuration;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.servlet.*;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.IOException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
@Configuration
public class CookieConsentConfiguration {
private static class CookieConsentFilter implements Filter {
private final ObjectMapper objectMapper;
private CookieConsentFilter(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
final HttpServletRequest req = (HttpServletRequest) request;
if (hasCookieConsent(req)) {
chain.doFilter(request, response);
} else {
chain.doFilter(request, new NoCookiesResponseWrapper((HttpServletResponse) response));
}
}
private boolean hasCookieConsent(HttpServletRequest request) {
try {
final Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
if (cookie.getName().equals("cookie_consent_level")) {
final String value = cookie.getValue();
final JsonNode json = this.objectMapper.readTree(URLDecoder.decode(value, StandardCharsets.US_ASCII));
return json.hasNonNull("strictly-necessary") && json.get("strictly-necessary").booleanValue();
} else if (cookie.getName().equals("cookie_consent_user_accepted")) {
return Boolean.parseBoolean(cookie.getValue());
}
}
}
} catch (Exception e) {
return false;
}
return false;
}
}
private static class NoCookiesResponseWrapper extends HttpServletResponseWrapper {
public NoCookiesResponseWrapper(HttpServletResponse response) {
super(response);
}
@Override
public void addCookie(Cookie cookie) {
// not allowed
}
@Override
public void setHeader(String name, String value) {
if (!name.equalsIgnoreCase("set-cookie")) {
super.setHeader(name, value);
}
}
@Override
public void addHeader(String name, String value) {
if (!name.equalsIgnoreCase("set-cookie")) {
super.addHeader(name, value);
}
}
}
@Bean
public Filter cookieConsentFilter(ObjectMapper objectMapper) {
return new CookieConsentFilter(objectMapper);
}
}
But this also didn't work. The JSESSIONID
is still being created.
Is there any way to disable the creation of this (or any) Cookie if some conditions aren't met?
Upvotes: 3
Views: 2965
Reputation: 2386
I found out that Strictly necessary cookies are allowed by default, without a requirement to show the user a cookie-layer before sending the cookie to the client. As I understand, you still have to display the cookie-layer to the user at the very first page impression.
That essentially means, to my understanding, that you are allowed to send Set-Cookie
-Headers for Strictly necessary cookies on the users first page load. On that page, the cookie-layer has to be shown, but Strictly necessary cookies may already exists at that point.
For more information, see: https://gdpr.eu/cookies/
First of all, we have to understand under what circumstances a JSESSIONID
is created.
It is created whenever some piece of code tries to obtain a session from the application server. That means, if we can structure our code in a way that it doesn't try to obtain a session from the application server, no JSESSIONID
-Cookie will be sent to the client.
But there are a few pitfalls. For some things, a session is the most necessary cookie of all:
I'm using spring-boot-starter-web
in Version 2.5.3
. I want a session to be only created if it's truly required, for one of the reasons I mentioned above: The users tries to login or the users accesses a site that requires CSRF Tokens to be created for security reasons.
The first relevant config can be found in my WebSecurityConfigurerAdapter
:
Setting the SessionCreationPolicy
to IF_REQUIRED
.
@Configuration
public static class GlobalWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
@Override
public void configure(WebSecurity web) {
web.ignoring().antMatchers("/webjars/**", "/css/**", "/js/**");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.and()
.headers()
.frameOptions()
.deny()
.and()
.sessionManagement()// these two lines
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.and()
.oauth2Login()
.and()
.logout()
.logoutSuccessUrl("/");
}
}
The second relevant thing in my case was to only embed the CSRF
-Tokens if they're truly required for the page.
I had a couple of thymeleaf templates of which I always used the same <head>
Section. In every of my templates, my <head>
Section included the following lines:
<meta name="_csrf" th:content="${_csrf.token}"/>
<meta name="_csrf_header" th:content="${_csrf.headerName}"/>
While this is still required for pages that perform POST
, PUT
, ... - Requests to my server, it is not required for most of my pages.
I removed the use of the CSRF
-Token in every thymeleaf template where it was not required.
Now, a session is only obtained (and a JSESSIONID
is created) from the application server once the user tries to access a page that includes a CSRF
-Token
Upvotes: 2