How to enable samesite for jsessionid cookie

后端 未结 4 1759
时光说笑
时光说笑 2020-12-15 21:32

How can I enable samesite for my web application which runs on wildfly as. Checked standalone.xml however could not find an appropriate tag within



        
相关标签:
4条回答
  • 2020-12-15 21:57

    My workaround, which works in JBoss EAP 7.2, is a custom handler. I use it as a global handler. But you can also use it in the jboss-web.xml. You need to play with the cookie implementation because undertow only allows Strict or Lax for samesite (it throws '"UT000162: Same-site attribute None is invalid. It must be Strict or Lax"' if you use 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);
             });
       }
    }
    

    handler configuration:

    <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>
    
    0 讨论(0)
  • 2020-12-15 22:01

    One workaround is to hack the SameSite setting into the cookie by using another attribute (e.g. comment):

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

    But because Undertow quotes the comment (and other) values when using version 0 or version 1 cookies, JBoss/WildFly needs to be running with the io.undertow.cookie.DEFAULT_ENABLE_RFC6265_COOKIE_VALIDATION system property set to true:

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

    This will give you the desired result:

    This approach is obviously hacky, and relies entirely on Undertow implementation details, so I'd recommend configuring on the web server or load balancer level instead.

    0 讨论(0)
  • 2020-12-15 22:20

    For Spring Boot with the currently latest release:

    If you do not have the latest spring-boot-starter-tomcat check the SameSiteCookies enum for value UNSET, if the value is missing you need a newer release because it will skip the value 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);
                }
            });
        }
    }
    
    0 讨论(0)
  • 2020-12-15 22:24

    As for now the Java Servlet 4.0 specification doesn't support the SameSite cookie attribute. You can see available attributes by opening javax.servlet.http.Cookie java class.

    However, there are a couple of workarounds. You can override Set-Cookie attribute manually.

    The first approach (using Spring's AuthenticationSuccessHandler):

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

    The second approach (using javax.servlet.Filter):

    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() {
    
        }
    }
    

    You can look at this demo project on the GitHub for more details on the configuration for org.springframework.security.web.authentication.AuthenticationSuccessHandler or javax.servlet.Filter.

    The WebSecurityConfig contains all the necessary configuration.

    Using addHeader is not guaranteed to work because basically the Servlet container manages the creation of the Session and Cookie. For example, both 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, above approaches will work in cases, when you redirect a user to another page after successful authentication.

    0 讨论(0)
提交回复
热议问题