首頁>科技>

cookie作為web瀏覽器的重要組成部分,經常被用於增加使用者的體驗,比如:記住登入名,購物車、跨域共享資料等,但同時也帶來了如CSRF攻擊的安全問題。自Chrome 80開始,谷歌對使用者實施了新的cookie政策,該政策添加了對Samesite的IETF標準的支援,並且預設將cookie的samesite級別設定為lax,這種的策略阻止了開發者對第三方cookie操作,對很多涉及到跨域的系統造成了巨大的影響。

什麼是cookie的samesite屬性?

samesite是從chrome 51開始,cookie新增的屬性,主要是用於防止csrf攻擊和使用者追蹤的,主要的屬性如下:(參考文件: Adobe Target和谷歌的SameSite Cookie政策 )

samesite屬性

透過屬性描述,可以瞭解到,samesite屬性主要是用於控制跨站cookie的操作,預設情況下,屬性為lax,在該屬性下只有少數方式可以使用跨域cookie,這對很多現有的跨域系統帶來巨大的困擾,需要透過將cookie的samesite設定為none才能讓新的瀏覽器下的cookie像在舊瀏覽器下的cookie一樣工作。同時,改配置必須與 Secure並行,也就是cookie必須透過https傳輸才能生效(該配置可以透過chrome配置去掉,服務端可以透過非https方式實現samesite=none的配置)。

如何解決(Java)

解決方案1:透過response寫入(非HTTPS請求可以生效)

public class CookieUtil {    public static void setDefaultCookie(HttpServletResponse response, String name, String value, int maxAge) {        try {            value = URLEncoder.encode(value, "UTF-8");        } catch (UnsupportedEncodingException e) {        }        String cookie = newCookie(name, value, "test.com", maxAge,"/","None");        response.addHeader("Set-Cookie",cookie);//        response.setHeader("Set-Cookie",cookie);//只會寫入一次    }    /**     * 獲取cookies 配置     * dd=dd; Max-Age=518400; Domain=test.com; Path=/; SameSite=None     * @param name     * @param value     * @param domain     * @param maxAge     * @param path     * @return     */    private static String newCookie(String name, String value, String domain, int maxAge,String path,String sameSite ){        String strTemp="${COOKIE_KEY}=${COOKIE_VALUE}; Max-Age=${MAXAGE}; Domain=${DOMAIN}; Path=${PATH}; SameSite=${SAMESITE};Secure";        String cookieStr=strTemp.replace("${COOKIE_KEY}",name).                replace("${COOKIE_VALUE}",value).                replace("${MAXAGE}",maxAge+"").                replace("${DOMAIN}",domain).                replace("${PATH}",path).                replace("${SAMESITE}",sameSite);        return cookieStr;    }}
注意: setHeader與addHeader的區別

透過response設定header的時候,可以透過setHeader與addHeader兩種方式進行設定,但是需要注意的是addHeader允許重複設定,而setHeader是不允許重複設定的,由於配置cookies的key為Set-Cookie,如果使用setHeader的方式進行設定,只會有一個cookie生效,所以在此需要用addHeader的方式進行設定。

setHeader原始碼

addHeader原始碼

解決方案2:基於springboot,修改tomcat配置 (參考Same-Site flag for session cookie in Spring Security)

經測試,該方案需要在springboot2.2.3版本以上,springboot內建版本為9.0.30才支援(更早版本未全部測試,2.1.*暫不支援)

@Component@Configurationpublic class WebConfiguration implements WebMvcConfigurer {    @Bean    public TomcatContextCustomizer sameSiteCookiesConfig() {       return new TomcatContextCustomizer() {            @Override            public void customize(Context context) {                final Rfc6265CookieProcessor cookieProcessor = new Rfc6265CookieProcessor();                cookieProcessor.setSameSiteCookies(SameSiteCookies.NONE.getValue());                context.setCookieProcessor(cookieProcessor);            }        };    }}        Cookie cookie=new Cookie("test","test");        cookie.setDomain("test.com");        cookie.setPath("/");//      cookie.setSecure(true);//新增後,http無法新增cookie        response.addCookie(cookie);

該配置是透過重寫定製化tomcat配置的方式,使所有的cookie都上samesite=None的配置。程式碼雖然在springboot2.1.6版本就可以用,但是並未生效。

網路方案(測試不一定通)

1、 透過filter重設cookie

網傳解決方案

該方案在看起來沒毛病,可是真實操作的時候,卻有致命的問題,無論addSameSiteCookieAttribute方法中的for迴圈操作了幾次,addHeader都不會改變header,cookie也不會寫回客戶端,這是因為,在tomcat下response在addHeader的時候,會判斷response是否提交,若已經提交,addHeader將被直接跳過(Filter 中 addCookie 不生效問題)。

上圖中addSameSiteCookieAttribute發生在doFilter之後,此時response的提交狀態為已提交,所以將不會再往header裡面新增內容,故功能無法實現。若將addSameSiteCookieAttribute提前,由於filter比controller早執行,無法拿到controller層的cookie,亦無法達到效果。

response的isCommitted是什麼時候提交的?

springMvc的RequestMappingHandlerAdapter進行請求處理後 ,會進行一次prepareResponse的操作,該操作會將response設定為已提交。

2、 使用AOP方式重設cookie

@Aspect@Componentpublic class CookiesAspect {    @After("execution(public * com.example.demo.controller.*Controller.*(..))")    public void handlerSameSite(JoinPoint joinPoint) throws Throwable {        addSameSite(joinPoint);    }    private void addSameSite(JoinPoint joinPoint) {        RequestAttributes ra = RequestContextHolder.getRequestAttributes();        ServletRequestAttributes sra = (ServletRequestAttributes) ra;        HttpServletResponse response = sra.getResponse();        Collection<String> headers = response.getHeaders(HttpHeaders.SET_COOKIE);        for (String header : headers) {            response.addHeader(HttpHeaders.SET_COOKIE, String.format("  %s;%s", header, "Samesite=None;Secure"));        }    }}

此段程式碼親測可行,可是存在一個不可靠的因數,因為addHeader是往header中新增cookie,不會刪除原有的cookie,這就會導致返回報文中,有對同一個cookie設定的情況。該情況在不同瀏覽器下體現不一樣。對於這種模稜兩可的因數,不用為妙。

請求監控

chrome80下的情況,如下:

chrome80下

360瀏覽器的情況如下:

360情況下

7
最新評論
  • 整治雙十一購物亂象,國家再次出手!該跟這些套路說再見了
  • 智慧社群風口已到來?