jessessionId cookie 的 Samesite 只能从响应中设置

Posted

技术标签:

【中文标题】jessessionId cookie 的 Samesite 只能从响应中设置【英文标题】:Samesite for jessessionId cookie can be set only from response 【发布时间】:2021-01-12 02:47:03 【问题描述】:

我正在尝试将相同站点设置为无;从 java filter 保护我的 jsessionid cookie。我已在响应集 cookie 标头中添加了此内容。更改后,请求 cookie jsessionId 相同。在响应中,jsessionId 被修改为 Samesite 属性 None 和安全。如果请求 jsessionId cookie 保持不变,它会起作用吗?

【问题讨论】:

检查这个使用GenericFilterBean / 临时重定向请求来解决同类问题***.com/questions/63939078/… 【参考方案1】:

调用 ServletResponse 方法:sendError、getWrite.flush()、sendRedirect、getOutputStream.Flush 提交响应,这意味着状态代码和标头将在 Set-cookie 标头更新之前写入。 ServletResponse.

解决方案 1:您可以将过滤器放在任何可能导致调用上述方法的过滤器之前,并在调用 filterChain.doFilter 之前修改标题

解决方案 2:在提交响应之前拦截对此方法的调用并更新标头。

    package com.cookie.example.filters.cookie;
    
    
    import com.google.common.net.HttpHeaders;
    import org.apache.commons.collections.CollectionUtils;
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.beans.factory.InitializingBean;
    import org.springframework.web.filter.DelegatingFilterProxy;
    
    import javax.annotation.Nonnull;
    import javax.servlet.*;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpServletResponseWrapper;
    import java.io.IOException;
    import java.io.PrintWriter;
    import java.util.Collection;
    import java.util.Collections;
    import java.util.List;
    
    /**
     * Implementation of an HTTP filter @link Filter which which allow customization of @literal Set-Cookie header.
     * customization is delegated to implementations of @link CookieHeaderCustomizer
     */
    public class CookieHeaderCustomizerFilter extends DelegatingFilterProxy implements InitializingBean 
    
      private final List<CookieHeaderCustomizer> cookieHeaderCustomizers;
    
      @Override
      public void afterPropertiesSet() throws ServletException 
        super.afterPropertiesSet();
        if(CollectionUtils.isEmpty(cookieHeaderCustomizers))
          throw new IllegalArgumentException("cookieHeaderCustomizers is mandatory");
        
      
    
      public CookieHeaderCustomizerFilter(final List<CookieHeaderCustomizer> cookieHeaderCustomizers) 
        this.cookieHeaderCustomizers = cookieHeaderCustomizers;
      
    
      public CookieHeaderCustomizerFilter() 
        this.cookieHeaderCustomizers = Collections.emptyList();
      
    
    
      /** @inheritDoc */
      public void destroy() 
      
    
      /** @inheritDoc */
      public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain)
        throws IOException, ServletException 
    
        if (!(request instanceof HttpServletRequest)) 
          throw new ServletException("Request is not an instance of HttpServletRequest");
        
    
        if (!(response instanceof HttpServletResponse)) 
          throw new ServletException("Response is not an instance of HttpServletResponse");
        
    
        chain.doFilter(request, new CookieHeaderResponseWrapper((HttpServletRequest) request, (HttpServletResponse)response ));
    
      
    
    
      /**
       * An implementation of the @link HttpServletResponse which customize @literal Set-Cookie
       */
      private class CookieHeaderResponseWrapper extends HttpServletResponseWrapper
    
        @Nonnull private final HttpServletRequest request;
    
        @Nonnull private final HttpServletResponse response;
    
    
        public CookieHeaderResponseWrapper(@Nonnull final HttpServletRequest req, @Nonnull final HttpServletResponse resp) 
          super(resp);
          this.request = req;
          this.response = resp;
    
        
    
        /** @inheritDoc */
        @Override
        public void sendError(final int sc) throws IOException 
          applyCustomizers();
          super.sendError(sc);
        
    
        /** @inheritDoc */
        @Override
        public PrintWriter getWriter() throws IOException 
          applyCustomizers();
          return super.getWriter();
        
    
        /** @inheritDoc */
        @Override
        public void sendError(final int sc, final String msg) throws IOException 
          applyCustomizers();
          super.sendError(sc, msg);
        
    
        /** @inheritDoc */
        @Override
        public void sendRedirect(final String location) throws IOException 
          applyCustomizers();
          super.sendRedirect(location);
        
    
        /** @inheritDoc */
        @Override
        public ServletOutputStream getOutputStream() throws IOException 
          applyCustomizers();
          return super.getOutputStream();
        
    
        private void applyCustomizers()
    
          final Collection<String> cookiesHeaders = response.getHeaders(HttpHeaders.SET_COOKIE);
    
          boolean firstHeader = true;
    
          for (final String cookieHeader : cookiesHeaders) 
    
            if (StringUtils.isBlank(cookieHeader)) 
              continue;
            
    
            String customizedCookieHeader = cookieHeader;
    
            for(CookieHeaderCustomizer cookieHeaderCustomizer : cookieHeaderCustomizers)
    
              customizedCookieHeader = cookieHeaderCustomizer.customize(request, response, customizedCookieHeader);
    
            
    
            if (firstHeader) 
              response.setHeader(HttpHeaders.SET_COOKIE,customizedCookieHeader);
              firstHeader=false;
             else 
              response.addHeader(HttpHeaders.SET_COOKIE, customizedCookieHeader);
            
    
          
    
        
    
      
    
    



  /**
   * Implement this interface and inject add it to @link SameSiteCookieHeaderCustomizer
   */
    public interface CookieHeaderCustomizer 
      String customize(@Nonnull final HttpServletRequest request, @Nonnull final HttpServletResponse response, @Nonnull final String cookieHeader);
    


  package com.cookie.example.filters.cookie;
  
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  import javax.annotation.Nonnull;
  import javax.servlet.http.HttpServletRequest;
  import javax.servlet.http.HttpServletResponse;
  
  /**
    *Add SameSite attribute if not already exist
    *SameSite attribute value is defined by property "cookie.sameSite"
   */
  public class SameSiteCookieHeaderCustomizer implements CookieHeaderCustomizer 
  
    private static final Logger LOGGER = LoggerFactory.getLogger(SameSiteCookieHeaderCustomizer.class);
  
    private static final String SAME_SITE_ATTRIBUTE_NAME ="SameSite";
  
    private static final String SECURE_ATTRIBUTE_NAME="Secure";
  
    private final SameSiteValue sameSiteValue;
  
    public SameSiteCookieHeaderCustomizer(SameSiteValue sameSiteValue) 
      this.sameSiteValue = sameSiteValue;
    
  
  
    @Override
    public String customize(@Nonnull final HttpServletRequest request, @Nonnull final HttpServletResponse response, @Nonnull final String cookieHeader) 
      StringBuilder sb = new StringBuilder(cookieHeader);
      if (!cookieHeader.contains(SAME_SITE_ATTRIBUTE_NAME)) 
        sb.append("; ").append(SAME_SITE_ATTRIBUTE_NAME).append("=").append(sameSiteValue.value);
      
      if(SameSiteValue.None == sameSiteValue && !cookieHeader.contains(SECURE_ATTRIBUTE_NAME))
        sb.append("; ").append(SECURE_ATTRIBUTE_NAME);
      
      return sb.toString();
    
  
    public enum SameSiteValue
  
      /**
       * Send the cookie for 'same-site' requests only.
       */
      Strict("Strict"),
      /**
       * Send the cookie for 'same-site' requests along with 'cross-site' top
       * level navigations using safe HTTP methods (GET, HEAD, OPTIONS, and TRACE).
       */
      Lax("Lax"),
      /**
       * Send the cookie for 'same-site' and 'cross-site' requests.
       */
      None("None");
  
      /** The same-site attribute value.*/
      private String value;
  
      /**
       * Constructor.
       *
       * @param attrValue the same-site attribute value.
       */
      SameSiteValue(@Nonnull final String attrValue) 
        value = attrValue;
      
  
      /**
       * Get the same-site attribute value.
       *
       * @return Returns the value.
       */
      public String getValue() 
        return value;
      
  
    
  
  

【讨论】:

以上是关于jessessionId cookie 的 Samesite 只能从响应中设置的主要内容,如果未能解决你的问题,请参考以下文章

sam文件解读

需要啥安全访问模块 (SAM)?

sam格式的简单了解

使用 SAM 时,AWS sam build 和 sam package 有啥区别?

SAM/BAM文件处理

SAM