Как включить Samesite для cookie jsessionid

Как я могу включить тот же сайт для моего веб-приложения, которое работает на wildfly как. Проверил standalone.xml, но не смог найти подходящий тег в

<servlet-container name = "default">
    <session-cookie http-only = "true" secure = "true"/>
    <jsp-config/>
</servlet-container>

Рассматривали ли вы использование Spring Session, поддерживающего cookie на одном сайте с версии 2.1.0.RELEASE? См. github.com/spring-projects/spring-session/blob/2.1.0.RELEASE‌ /…

snieguu 17.05.2019 21:58

проверьте тот, который использовал GenericFilterBean / запрос временного перенаправления для решения той же проблемы stackoverflow.com/questions/63939078/…

ThilankaD 28.10.2020 06:15
SQL Injection: Атаки в реальной жизни и как это вредит бизнесу
SQL Injection: Атаки в реальной жизни и как это вредит бизнесу
Один-единственный вредоносный запрос может нанести ущерб вашему бизнесу. Уязвимости вашего кода могут привести к:
19
2
35 325
7

Ответы 7

На данный момент спецификация Java Servlet 4.0 не поддерживает атрибут cookie SameSite. Вы можете увидеть доступные атрибуты, открыв класс Java javax.servlet.http.Cookie.

Однако есть несколько обходных путей. Атрибут Set-Cookie можно переопределить вручную.

Подход №1 (с использованием настраиваемого Spring HttpFirewall и оболочки вокруг запроса):

Вам нужно обернуть запрос и настроить файлы cookie сразу после создания сеанса. Вы можете добиться этого, определив следующие классы:

один bean-компонент (вы можете определить его внутри SecurityConfig, если хотите хранить все в одном месте. Я просто поместил на него аннотацию @Component для краткости)

package hello.approach1;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.security.web.firewall.FirewalledRequest;
import org.springframework.security.web.firewall.HttpFirewall;
import org.springframework.security.web.firewall.RequestRejectedException;
import org.springframework.stereotype.Component;

@Component
public class CustomHttpFirewall implements HttpFirewall {

    @Override
    public FirewalledRequest getFirewalledRequest(HttpServletRequest request) throws RequestRejectedException {
        return new RequestWrapper(request);
    }

    @Override
    public HttpServletResponse getFirewalledResponse(HttpServletResponse response) {
        return new ResponseWrapper(response);
    }

}

первый класс-оболочка

package hello.approach1;

import java.util.Collection;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.springframework.http.HttpHeaders;
import org.springframework.security.web.firewall.FirewalledRequest;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

/**
 * Wrapper around HttpServletRequest that overwrites Set-Cookie response header and adds SameSite=None portion.
 */
public class RequestWrapper extends FirewalledRequest {

    /**
     * Constructs a request object wrapping the given request.
     *
     * @param request The request to wrap
     * @throws IllegalArgumentException if the request is null
     */
    public RequestWrapper(HttpServletRequest request) {
        super(request);
    }

    /**
     * Must be empty by default in Spring Boot. See FirewalledRequest.
     */
    @Override
    public void reset() {
    }

    @Override
    public HttpSession getSession(boolean create) {
        HttpSession session = super.getSession(create);

        if (create) {
            ServletRequestAttributes ra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            if (ra != null) {
                overwriteSetCookie(ra.getResponse());
            }
        }

        return session;
    }

    @Override
    public String changeSessionId() {
        String newSessionId = super.changeSessionId();
        ServletRequestAttributes ra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (ra != null) {
            overwriteSetCookie(ra.getResponse());
        }
        return newSessionId;
    }

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

второй класс-оболочка

package hello.approach1;

import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

/**
 * Dummy implementation.
 * To be aligned with RequestWrapper.
 */
public class ResponseWrapper extends HttpServletResponseWrapper {
    /**
     * Constructs a response adaptor wrapping the given response.
     *
     * @param response The response to be wrapped
     * @throws IllegalArgumentException if the response is null
     */
    public ResponseWrapper(HttpServletResponse response) {
        super(response);
    }
}

Подход № 2 (с использованием Spring AuthenticationSuccessHandler):

This approach doesn't work for basic authentication. In case basic authentication, response is flushed/committed right after controller returns response object, before SameSiteFilter#addSameSiteCookieAttribute is called.

package hello.approach2;

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

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.http.HttpHeaders;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;

public class AuthenticationSuccessHandlerImpl implements AuthenticationSuccessHandler {

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {
        addSameSiteCookieAttribute(response);    // add SameSite=strict to Set-Cookie attribute
        response.sendRedirect("/hello"); // redirect to hello.html after success auth
    }

    private void addSameSiteCookieAttribute(HttpServletResponse response) {
        Collection<String> headers = response.getHeaders(HttpHeaders.SET_COOKIE);
        boolean firstHeader = true;
        for (String header : headers) { // there can be multiple Set-Cookie attributes
            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"));
        }
    }
}

Подход № 3 (с использованием javax.servlet.Filter):

This approach doesn't work for basic authentication. In case basic authentication, response is flushed/committed right after controller returns response object, before SameSiteFilter#addSameSiteCookieAttribute is called.

package hello.approach3;

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;

import org.springframework.http.HttpHeaders;

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);
        addSameSiteCookieAttribute((HttpServletResponse) response); // add SameSite=strict cookie attribute
    }

    private void addSameSiteCookieAttribute(HttpServletResponse response) {
        Collection<String> headers = response.getHeaders(HttpHeaders.SET_COOKIE);
        boolean firstHeader = true;
        for (String header : headers) { // there can be multiple Set-Cookie attributes
            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"));
        }
    }

    @Override
    public void destroy() {

    }
}

Подход №4 (если вы используете Tomcat 9.0.21 / Tomcat 8.5.42 или более поздние версии)

В вашем веб-приложении внутри папки META-INF создайте файл context.xml со следующим содержимым:

Установка для SameSite значения none доступна начиная с Tomcat 9.0.28 / Tomcat 8.5.48)

Подробнее см. этот запрос на перенос.

Демонстрационный проект

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

SecurityConfig содержит всю необходимую конфигурацию.

Using addHeader is not guaranteed to work because basically the Servlet container manages the creation of the Session and Cookie. For example, the second and third approaches won't work in case you return JSON in response body because application server will overwrite Set-Cookie header during flushing of response. However, second and third approaches will work in cases, when you redirect a user to another page after successful authentication.

Pay attention that Postman doesn't render/support SameSite cookie attribute under Cookies section (at least at the time of writing). You can look at Set-Cookie response header or use curl to see if SameSite cookie attribute was added.

После 20 часов отладки я наткнулся на этот ответ. Проблема решена. Большое спасибо, Евгений

Venky 25.02.2020 14:02

@ Венки, я рад, что смог помочь

Eugene Maysyuk 25.02.2020 20:41

Как это должно работать, если doFilter запускается до изменения файла cookie? Я пытаюсь это сделать, и мои файлы cookie не изменяются, что бы я ни делал

Lightheaded 25.03.2020 13:53

@Lightheaded, убедитесь, что вы вызываете addSameSiteCookieAttribute () после метода doFilter (). Это гарантирует, что запрос был обработан сервлетом и заголовок «Set-Cookie» уже присутствует в ответе (и готов к перезаписи).

Eugene Maysyuk 25.03.2020 15:27

Хорошо, я думаю, моя проблема в другом (например, Tomcat перезаписывает cookie). Спасибо!

Lightheaded 25.03.2020 21:03

@Lightheaded, не могли бы вы описать, как это произошло в вашем случае, когда Tomcat перезаписал заголовок "Set-Cookie", когда у вас будет дополнительная информация о проблеме? Мне может потребоваться обновить сообщение дополнительной информацией. Кстати, у вас есть образец приложения, чтобы я мог воспроизвести проблему, с которой вы столкнулись?

Eugene Maysyuk 26.03.2020 08:20

Приложение, с которым я имел дело, было довольно большим и работало со старыми версиями spring (но не с Spring boot), поэтому, к сожалению, у меня нет примеров. В моем случае любые изменения, которые я сделал с файлом cookie программно, не распространялись (т.е. .addHeader() вообще не повлиял). В конце концов, я решил переместить серверную часть в тот же домен и не нашел способа устранить основную причину.

Lightheaded 27.03.2020 13:53

@Lightheaded, не могли бы вы создать образец проекта, который воспроизводит вашу проблему, и загрузить его на github? Я мог бы взглянуть на это и посмотреть, смогу ли я вам помочь.

Eugene Maysyuk 27.03.2020 15:46

К сожалению, создание примера проекта для меня - серьезная задача. Я сделаю это, если у меня будет время в ближайшие недели, но, поскольку я все равно решил переписать приложение, это, скорее всего, в данный момент не является разумным вложением времени для меня. Однако я очень ценю ваше предложение о помощи! Спасибо за это!

Lightheaded 29.03.2020 20:26

Вы знаете, почему мои заголовки переменных всегда пусты, даже если я получаю заголовок Set-Cookie в инспекторе?

WaLinke 16.07.2020 17:11

@WaLinke, могу я взглянуть на ваш код? У вас есть образец кода на GitHub?

Eugene Maysyuk 17.07.2020 19:04

Заголовки отправляются первыми в HTTP-запросе, поэтому, если ответ уже был (частично) передан клиенту, это не сработает.

cocorossello 31.08.2020 10:57

у нас есть только страницы jsp без сервлета, как исправить это в jsp?

Hosein Aqajani 26.09.2020 12:48

@EugeneMaysyuk Я использую первый подход, и, к сожалению, заголовок Set-Cookie еще не отображается в ответе ... Я открыл вопрос, мои классы там, не могли бы вы мне помочь? Тай! stackoverflow.com/questions/67320068/…

Andre 29.04.2021 22:54

@Andre, я ответил на ваш вопрос.

Eugene Maysyuk 30.04.2021 14:44

Один из обходных путей - взломать настройку SameSite в cookie с помощью другого атрибута (например, comment):

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

Но поскольку Undertow цитирует значения комментариев (и других) при использовании файлов cookie версии 0 или 1, JBoss / WildFly должен работать с системным свойством io.undertow.cookie.DEFAULT_ENABLE_RFC6265_COOKIE_VALIDATION, установленным на true:

 ./bin/standalone.sh -Dio.undertow.cookie.DEFAULT_ENABLE_RFC6265_COOKIE_VALIDATION=true

Это даст вам желаемый результат: cookies

Этот подход, очевидно, является хакерским и полностью зависит от деталей реализации Undertow, поэтому я бы рекомендовал вместо этого настраивать его на уровне веб-сервера или балансировщика нагрузки.

В качестве альтернативы вы можете установить атрибут rfc6265-cookie-validation = true в теге subsystem-> server-> default-server-> http-listener <http-listener name = "default" socket-binding = "http" max-post- size = "10485760000" rfc6265-cookie-validation = "true" redirect-socket = "https" enable-http2 = "true" />

Rahil Husain 27.08.2020 12:17

Для Spring Boot с последней версией на данный момент:

Если у вас нет последней версии spring-boot-starter-tomcat, проверьте перечисление SameSiteCookies на значение UNSET, если значение отсутствует, вам нужна более новая версия, потому что она пропустит значение SameSite=None.

@Component
public class SameSiteTomcatCookieProcessorCustomizationBean implements WebServerFactoryCustomizer<TomcatServletWebServerFactory>
{
    @Override
    public void customize(TomcatServletWebServerFactory server) {

        server.getTomcatContextCustomizers().add(new TomcatContextCustomizer()
        {
            @Override
            public void customize(Context context)
            {
                Rfc6265CookieProcessor cookieProcessor = new Rfc6265CookieProcessor();
                cookieProcessor.setSameSiteCookies("None");
                context.setCookieProcessor(cookieProcessor);
            }
        });
    }
}

Этот ответ мне помог! Мы не используем весеннюю сессию, поэтому другие не применимы.

Anna Ira Hurnaus 27.08.2020 14:31

Мой обходной путь, который работает в JBoss EAP 7.2, - это настраиваемый обработчик. Я использую его как глобальный обработчик. Но вы также можете использовать его в jboss-web.xml. Вам нужно поиграть с реализацией cookie, потому что undertow допускает только Strict или Lax для одного и того же сайта (он выдает '"UT000162: Атрибут Same-site None недопустим. Он должен быть Strict или Lax"', если вы используете cookie.setSameSiteMode ("None" ))

import io.undertow.server.HttpHandler;
import io.undertow.server.HttpServerExchange;
import io.undertow.server.handlers.Cookie;
import java.lang.reflect.Proxy;
import java.util.Map;

public class CookieSameSiteHandler implements HttpHandler
{
   private  HttpHandler next;

   public CookieSameSiteHandler(HttpHandler next){
      this.next = next;
   }

   @Override
   public void handleRequest(final HttpServerExchange exchange)
      throws Exception
   {
      exchange.addResponseCommitListener(serverExchange -> {
         for (Map.Entry<String, Cookie> responcecookie : serverExchange.getResponseCookies().entrySet()){
            serverExchange.getResponseCookies().replace(responcecookie.getKey(), proxyCookie(responcecookie.getValue()));
         }
      });
      next.handleRequest(exchange);
   }

   private Cookie proxyCookie(Cookie cookie)
   {
      return (Cookie)Proxy.newProxyInstance(
         cookie.getClass().getClassLoader(),
         cookie.getClass().getInterfaces(),
         (proxy, method, args) -> {
            if ("isSameSite".equals(method.getName())){
               return true;
            }
            if ("getSameSiteMode".equals(method.getName()) && cookie.getSameSiteMode() == null){
               return "None";
            }
            if ("isSecure".equals(method.getName()) && cookie.getSameSiteMode() == null){
               return true;
            }
            return method.invoke(cookie, args);
         });
   }
}

конфигурация обработчика:

<subsystem xmlns = "urn:jboss:domain:undertow:7.0" default-virtual-host = "default-host">
    <buffer-cache name = "default"/>
    <server name = "default-server" default-host = "default-host">
        ...
        <host name = "default-host" alias = "localhost,example.com">
            ...
            <filter-ref name = "cookiehandler"/>
            ...
        </host>
    </server>
    ...
    <filters>
        <filter class-name = "nl.myownstuff.handler.CookieSameSiteHandler" module = "nl.myownstuff.undertow" name = "cookiehandler"/>
    </filters>
</subsystem>

Решение для Wildfly 19.1.0 и новее:

$ cat src/main/webapp/WEB-INF/undertow-handlers.conf
samesite-cookie(mode=Lax)

Ресурс: https://www.wildfly.org/news/2020/05/04/WildFly-1910-Released/

У меня возникли проблемы с принятым решением из-за отсутствия заголовка «Set-Cookie» ни для одного из вызовов.

Таким образом, я попробовал другое решение от StackOverflow, конечно, изменив флаги по мере необходимости:

Добавление флагов в существующий файл cookie Jsessionid

Если вы используете WildFly 19 или новее, рекомендуется определить политику SameSite в файле undertow-handlers.conf. Это довольно гибко, поскольку вы можете определить веб-контекст, в котором будет использоваться политика SameSite, и шаблон регулярного выражения для файлов cookie. Пример:

path(/app2)->samesite-cookie(mode=Lax, cookie-pattern=abc*)

С другой стороны, для приложений Tomcat вы можете добавить файл META-INF / context.xml с атрибутом sameSiteCookies, как в этом примере:

<Context>
   <CookieProcessor sameSiteCookies = "strict" />
</Context>

Некоторые ссылки: https://github.com/apache/tomcat/pull/162

Как установить атрибут SameSite в веб-приложениях

Другие вопросы по теме