Весной автоматически добавьте безопасный флаг в файл cookie JSESSIONID

У меня есть сервер приложений Tomcat, который находится за nginx. SSL заканчивается на nginx. Приложение Spring web-mvc, которое развернуто на tomcat, должно установить флаг безопасности на JSESSIONID. Было бы здорово, если бы у Spring было автоматическое определение для этого, поэтому я не буду беспокоиться во время разработки, потому что у меня там нет SSL.

Есть ли способ сказать пружине, чтобы установить флаг автоматически?

Я использую JavaConfig для настройки приложения и использую Maven для создания развертываемого war-файла.

Я уже проверил это, но это выглядит как-то уродливо и статично: установите флаг "secure" в cookie-файл JSESSION id

7 ответов

Решение

Когда вы используете spring-session, например, чтобы сохранить ваш сеанс в reddis, это действительно делается автоматически. Файл cookie создается org.springframework.session.web.http.CookieHttpSessionStrategy который в CookieHttpSessionStrategy#createSessionCookie проверяет, поступил ли запрос по HTTPS, и соответственно устанавливает безопасность:

sessionCookie.setSecure(request.isSecure());

Если вы не используете весеннюю сессию, вы можете настроить безопасные куки, используя ServletContextInitializer, Используйте свойство приложения, чтобы установить его в true/false в зависимости от профиля.

@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 (используется в dev, когда профиль 'prod' не активен):

secure.cookie=false

application-prod.properties (используется только при активном профиле prod, перезаписывает значение в application.properties):

secure.cookie=false

запустите ваше приложение на сервере prod с помощью:

--spring.profiles.active=prod

Похоже на некоторые усилия, если вы до сих пор не работали с профилями, но вам, скорее всего, понадобится профиль для среды prod, так что это действительно того стоит.

Если вы используете Spring Boot, для этого есть простое решение. Просто установите следующее свойство в вашем application.properties:

server.servlet.session.cookie.secure=true

Источник: Spring docs - Приложение A. Общие свойства приложения

Если у вас есть среда с HTTPS, а другая - без нее, вам потребуется установить значение false в профилях без HTTPS. В противном случае безопасный cookie игнорируется.

В вашем application.yml просто добавьте

server:
  session:
    cookie:
      secure: true

За nginx в качестве конечной точки ssl это нетривиальная задача: защищенное соединение должно быть обнаружено заголовком nginx (X-Forwarded-Proto: httpsсм. Использование заголовка Forwarded)
Но это легко решить с помощью конфигурации nginx:

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

У нас есть приложение Spring Boot 2.3, которое использует HTTPS для NGINX и HTTP между NGINX и Tomcat.

Даже с этой настройкой свойства Spring:

      server:
    servlet:
        session:
            cookie:
                secure: true

... Secureфлаг не устанавливается в файле cookie JSESSIONID при доступе к приложению через HTTP. Вы можете проверить это, запустив приложение локально и нажав Tomcat напрямую, используя HTTP против HTTP.

Я обнаружил, что добавление этого в конфигурацию устанавливает флаг Secure для HTTP и HTTPS, что устраняет нашу проблему при размещении NGINX перед Tomcat с использованием 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);
        }
    };
}

Это работает для меня

      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);
}
}

Добавить другой вариант

Вы можете использовать ServletContextInitializer для установки безопасного файла cookie и флага только http

@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);
        }
    };
}
Другие вопросы по тегам