Add secure flag to JSESSIONID cookie in spring automatically
Asked Answered
B

7

18

I have a tomcat application server that is behind a nginx. SSL terminates on the nginx. The Spring web-mvc application that is deployed on the tomcat should set the secure flag on the JSESSIONID. It would be cool if spring has some automatic detection for this so I don't get bothered during development because I don't have SSL there.

Is there a way to tell spring to set the flag automatically?

I use JavaConfig to setup the application and use Maven to create a deployable war-file.

I have checked this already, but this looks somehow ugly and static: set 'secure' flag to JSESSION id cookie

Breakfast answered 31/8, 2016 at 14:54 Comment(0)
C
18

When you use spring-session, e.g. to persist your session in reddis, this is indeed done automatically. The cookie is than created by org.springframework.session.web.http.CookieHttpSessionStrategy which in CookieHttpSessionStrategy#createSessionCookie checks if the request comes via HTTPS and sets secure accordingly:

sessionCookie.setSecure(request.isSecure());

If you do not use spring-session, you can configure secure cookies using a ServletContextInitializer. Use a application property, to set it to true/false depending on a profile.

@Bean
public ServletContextInitializer servletContextInitializer(@Value("${secure.cookie}") boolean secure) {
    return new ServletContextInitializer() {

        @Override
        public void onStartup(ServletContext servletContext) throws ServletException {
            servletContext.getSessionCookieConfig().setSecure(secure);
        }
    };
}

application.properties (used in dev when profile 'prod' is not active):

secure.cookie=false

application-prod.properties (only used when profile 'prod' is active, overwrites value in application.properties):

secure.cookie=false

start your application on the prod server with :

--spring.profiles.active=prod

Sounds like some effort, if you have not worked with profiles so far, but you will most likely need a profile for prod environment anyway, so its really worth it.

Carthage answered 31/8, 2016 at 20:37 Comment(5)
The CookieHttpSessionStrategy mention that the secure flag is added automatically when HttpServletRequest#isSecure() returns true. How does that method determine that the request was indeed send via https if the ssl terminates on the nginx and not on the tomcat? Or the other way round how do I tell the tomcat that the request was ssl encrypted at some point?Breakfast
Generally you would have your ssl terminator set request header to of x-forwarded-proto = https, and then your web server could use that to mark the request as isSecure.Complicate
setting secure.cookie to true/false doesn't seem to be doing anything for us. We use nginx and an aws loadbalancer in front of that so we need to force this to be secure somehow. Is there a way to force this to be on?Voiceful
generally you have own properties for dev and prod, so just set server.servlet.session.cookie.secure=true in application-prod.property/yamlGoldschmidt
Why would you care if the Cookie is flagged as Secure for HTTP and HTTPS? Why not do it all the time and not worry about the separate configurations for PROD and DEV?Bethelbethena
C
18

If you are using Spring Boot, there is a simple solution for it. Just set the following property in your application.properties:

server.servlet.session.cookie.secure=true

Source: Spring docs - Appendix A. Common application properties

If you have some environment with HTTPS and some without it, you will need to set it to false in profiles without HTTPS. Otherwise the Secure cookie is ignored.

Continuum answered 12/9, 2018 at 12:45 Comment(1)
Why do you need to set it as false when using HTTP? Who cares if it has the Secure flag on HTTP? Sure, it won't be Secure, but it doesn't cause harm and I don't believe goes against the RFC.Bethelbethena
D
3

in your application.yml just add

server:
  session:
    cookie:
      secure: true
Dovekie answered 19/4, 2018 at 22:52 Comment(3)
Use server: servlet: session: cookie: secure: true instead.Lettyletup
server.session.cookie.secure=true in application.properties works for me.Landaulet
This only works for me if the app is accessed via HTTPS... not HTTP. The OP is using HTTP to hit Tomcat from NGINX... maybe this behavior has changed in later versions of Spring Boot... I don't see the harm of always making the JSESSIONID Secure regardless of the protocol uses.dBethelbethena
G
1

Behind nginx as ssl terminal point it is not trivial task: secured connection must be detected by nginx header (X-Forwarded-Proto: https, see Using the Forwarded header)
But it is easy solved by nginx config:

if ($scheme = http) {
    return 301 https://$http_host$request_uri;
}
proxy_cookie_path / "/; secure";
Goldschmidt answered 20/7, 2020 at 19:11 Comment(0)
B
1

We have a Spring Boot 2.3 app that uses HTTPS to NGINX and HTTP between NGINX and Tomcat.

Even with this Spring property setting:

server:
    servlet:
        session:
            cookie:
                secure: true

... the Secure flag is not being set on the JSESSIONID cookie when the app is accessed via HTTP. You can test this by running the app locally and hitting Tomcat directly using HTTP vs HTTP.

I found that adding this to the config sets the Secure flag for HTTP and HTTPS which fixes our issue when putting NGINX in front of Tomcat using HTTP:

/**
 * Fix for GCP... since we use HTTP internally in Kubernetes, Spring will not make JSESSIONID Secure, but this will.
 *
 * See https://www.javafixing.com/2021/11/fixed-add-secure-flag-to-jsessionid.html
 *
 * @return
 */
@Bean
public ServletContextInitializer servletContextInitializer() {
    return new ServletContextInitializer() {

        @Override
        public void onStartup(ServletContext servletContext) throws ServletException {
            servletContext.getSessionCookieConfig().setSecure(true);
        }
    };
}
Bethelbethena answered 17/5, 2022 at 17:7 Comment(1)
in my case I was subclassing SpringBootServletInitializer - when overriding its own onStartup method, you would need to call super.onStartup method before modifying sessionCookieConfig, otherwise the application won't startDisorganization
E
0

Add another option

You can use a ServletContextInitializer to set secure cookie and http only flag

@Bean
public ServletContextInitializer servletContextInitializer() {
    return new ServletContextInitializer() {
        @Override
        public void onStartup(ServletContext servletContext) throws ServletException {
            servletContext.setSessionTrackingModes(Collections.singleton(SessionTrackingMode.COOKIE));
            SessionCookieConfig sessionCookieConfig = servletContext.getSessionCookieConfig();
            sessionCookieConfig.setHttpOnly(true);
            sessionCookieConfig.setSecure(true);
        }
    };
}
Exceeding answered 4/6, 2019 at 3:15 Comment(0)
W
0

It's working for me

public class WebInitializer implements WebApplicationInitializer {

@Override
public void onStartup(ServletContext servletContext) throws ServletException {
    AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
    ctx.register(AppConfig.class);
    ctx.setServletContext(servletContext);
    Dynamic servlet = servletContext.addServlet("dispatcher", new DispatcherServlet(ctx));
    servlet.addMapping("/");
    servlet.setLoadOnStartup(1);

    servletContext.setSessionTrackingModes(Collections.singleton(SessionTrackingMode.COOKIE));
    SessionCookieConfig sessionCookieConfig = servletContext.getSessionCookieConfig();
    sessionCookieConfig.setHttpOnly(true);
    sessionCookieConfig.setSecure(true);
}
}
Walrus answered 26/4, 2022 at 16:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.