samesite-cookie詳解(譯文)

Cookie是便於向網站添加持久化狀態的方式之一。隨着時間推移,它們的能力得到了擴展和進化,也造成了很多歷史遺留問題。為了解決這個問題,瀏覽器產商(包括Chrome,Firefox,和Edge)改變了他們的處理邏輯以加強個人隱私的默認配置。

每一個cookie都是擁有一些為了控制何時何處被使用的鍵值對。你可能已經通過這些屬性去設置過期時間或者指明當前cookie只能在https協議下傳輸。服務器通過在響應中攜帶Set-Cookie頭來設置cookie。想要了解詳細信息,可以深入閱讀RFC6265bis,現在只是快速的回顧一下。

現在假設你的博客網站需要向用戶推送一個「最新內容」的提示。用戶可能會取消這個提示並且一段時間內他們也不會再看這個提示。你可以將這種喜好設置在一個cookie中,給它設置一個月(2,600,000 seconds)後到期,並且只能在HTTPS協議下傳輸。這個頭部可能看起來像這樣:

Set-Cookie: promo_shown=1; Max-Age=2600000; Secure

server set-cookie
服務器通過Set-Cookie頭來設置cookie

當你的讀者正在訪問一個做了上面這些設置的頁面,進一步說他們正在使用HTTPS進行連接並且這個cookie存續時間小於一個月,那麼他們的瀏覽器就會請求里攜帶如下的頭部:

Cookie: promo_shown=1

browser send back cookie
瀏覽器通過Cookie頭回傳cookie

在javascript中通過使用document.cookie可以讀取或者添加站點的cookie。對document.cookie進行賦值操作將會覆蓋對應鍵的cookie。舉個例子,嘗試在你的瀏覽器javascript console中輸入以下語句:

> document.cookie = "promo_shown=1; Max-Age=2600000; Secure"
< "promo_shown=1; Max-Age=2600000; Secure"

讀取document.cookie將會打印出所有能夠在當前環境中獲取到的cookie,每個cookie通過一個分號分隔

> document.cookie;
< "promo_shown=1; color_theme=peachpuff; sidebar_loc=left"

read cookie in javascript
JavaScript中可以通過使用document.cookie獲取cookie

如果你嘗試在一些流行的站點上讀取cookie,你會明顯發現它們中大多數設置了超過3個cookie。在大多數場景下,這些cookie將會根據一系列因素攜帶在對應域名的請求中。對於你的用戶來說,一般上行帶寬一般比下行帶寬擁有更多的限制,所以將大量攜帶在cookie對外的請求上將會導致將會導致請求獲取第一個位元組的延遲。為了合理控制設置的cookie數量。通過設置Max-Age屬性來幫助保證cookie在不需要的時候及時被清除掉。

什麼是自有和第三方cookie?

重新訪問前面一系列站點,很可能會發現有些cookie會在很多不同域名下出現,不僅僅是當前正在訪問站點域名。當前站點域名(顯示在瀏覽器地址欄裏面的域名),被稱為自有域名。同樣的,那些來自當前站點外域名的cookie被稱為第三方域名。這只是相對用戶的角度並不是一個絕對的標籤,同樣的cookie既可能是自有的也可能是第三方的,取決於用戶當前訪問的站點。

cookies come from different domains
一個頁面上的cookie可能來自多個不同域名

繼續前面的例子,假設你的一篇博客當中有一張極其可愛的小貓圖片,這張圖片存儲在/blog/img/amazing-cat.png下。由於這張圖片太可愛了,其他人在他們的網站上直接使用了它。如果一個用戶訪問過你的博客將會有promo_shown cookie,當他在其他網站上看到這張直接被引用的圖片時,圖片的請求將會攜帶這個cookie。這並不是對任何人都特別有用,因為promo_shown在其他人的網站上並不是對任何請求都有用,這樣只會增加請求的負載。

如果這並不是有意達成的效果,那在什麼情況下你需要這樣了。這是一種允許在第三方站點環境上保持會話狀態的機制。例如,如果在你的站點頁面上添加了一個YouTube video,那麼訪問者將會在播放器中看到一個”watch later”的操作項。如果訪問者已經登陸了YouTube,第三方cookie將會使得用戶通過一步點擊”watch later”按鈕收藏這個視頻到YouTube供後續觀看,而不需要提醒他們登陸YouTube或者離開當前站點並跳轉到YouTube。

third-party cookie used for maintain state
在第三方環境下訪問不同的頁面cookie將被傳輸

萬維網文化屬性之一是開放共識。這使得許多人創造自己的內容和應用程序成為可能。然而這也帶來了一些安全和隱私風險。跨站請求偽造攻擊(Cross-site request forgery attack-csrf)製造依賴於那些cookie將會被攜帶在針對指定域名的請求中,而不論這些請求具體由誰觸發。舉個例子,當你訪問了evil.example並且它可以觸發your-blog.example的請求,那麼瀏覽器將會很樂意攜帶上關聯的cookie。如果你的博客對這些請求的驗證不夠仔細那麼evil.example可以觸發刪除和添加內容的任何操作。

用戶現在更加清醒的意識到cookie怎樣被用來跟蹤他們在不同頁面的活動軌跡。然而直到現在仍然沒有一種明確的方式表明對cookie本來用意。你的promo_showncookie本應該只被用在自有站點環境下,而嵌入到第三方站點的組件所攜帶的會話cookie是被用來保持會話狀態。

使用SameSite明確表明cookie的用途

SameSite屬性的引入(定義在RFC6265bis)允許你表明你的cookie被用在第三方或者自己的站點環境下。明白站點的確切意義將會大有裨益。站點是域名後綴和它前面部分的混合。例如,www.web.dev域名站點是web.dev域名站點的一部分。

關鍵詞same-site

如果用戶在www.web.dev發起了一個向static.web.dev的圖片請求這是一個same-site請求。

public suffix list定義了這些,不僅是像.com這樣的頂級域名也包括像github.io這樣的域名。這使得your-project.githubmy-project.github被認為為不同的站點。

關鍵詞cross-site

如果用戶在your-project.github.io站點發起了一個向my-project.github.io的請求,這是一個cross-site請求。

在cookkie中引入SameSite屬性三種控制這種行為的方式。你可以不指定指定這個屬性,或者通過使用StrictLax來限制cookie只在same-site請求下攜帶。

如果你設置SameSite屬性為Strict,那麼你的cookie將會只在自有環境下發送。在用戶看來,那個cookie只會在匹配當前顯示在地址欄的站點時才會被發送。所以,假設Promo_showncookie如下設置:

Set-Cookie: promo_shown=1; SameSite=Strict

那麼當你的用戶在你的站點上是,cookie將會如預期被發送。然而當從一個指向你站點的引用鏈接,詳細地說當從另一個站點或者從通過一個朋友發給你的一個鏈接,在初始請求里並不會這個cookie並不會被發送。當你擁有一些關聯後置於初始導航你站點功能的cookie的情形下,例如修改密碼或者下訂單的情形下,這是極其有用的,但是對於promo_shown這個功能限制太多。如果你的用戶通過鏈接進入你的站點,他們希望cookie能被發送,這樣他們的設置能夠生效。

當你的用戶初始導航到你的網站上時cookie將會被發送,這就是SameSite=Lax的使用場景。讓我們繼續回顧那個引用小貓圖片的例子,那個例子中其他站點正在引用你的內容。他們直接使用你的小貓圖片並且提供了一個指向你的原始文章的鏈接地址。

<p>Look at this amazing cat!</p>
<img src="//blog.example/blog/img/amazing-cat.png" />
<p>Read the <a href="//blog.example/blog/cat.html">article</a>.</p>

cookie這樣設置:

Set-Cookie: promo_shown=1; SameSite=Lax

當瀏覽者在其他人的博客上請求amzing-cat.png,cookie將不會被發送。然而當讀者通過一個點擊一個指向你站點cat.html的鏈接時,cookie將會被發送。這樣的話,使得Lax成為設置影響網站外觀cookie的好選擇而Strict將會對用戶那些關聯到當前操作的cookie極其有用。

注意

StrictLax對於你的站點安全都不是一個完整的解決方案。cookie將會作為用戶輸入的一部分,你需要像對待其他用戶輸入一樣。意味着,需要對這些輸入進行脫敏和驗證。絕對不要將服務端的重要數據存儲在cookie中。

最後還有一個不設置這個值的默認方式,那麼cookie將會在所有上下文情形下被發送。在最新的RFC6265bis草案中這種方式被SameSite=None這個新值明確表達。這表明你可以通過設置SameSite=None來明確表明你希望cookie在所有第三方上下文環境下被發送。

!(None Lax Strict)[//webdev.imgix.net/samesite-cookies-explained/samesite-none-lax-strict.png]
通過NoneLaxStrict來明確表明cookie的使用上下文

★ 如果你提供了供其他網站使用的服務,例如嵌入式組件、嵌入式多媒體內容、聯合程序、廣告、跨站登陸那麼你需要使用None來明確表明的意圖。

未設置SameSite屬性默認行為的變化

儘管SameSite屬性得到廣泛支持,很不幸的是並沒有很廣泛的被開發者使用。cookie在所有地方都會被發送的開放默認行為使得所有場景都能正常工作,但是這樣提高了用戶受到CSRF和意外信息泄漏的風險指數。為了號召開發者明確表明他們的意圖並且提供用戶更高的安全使用體驗,IETF發佈了一個提議,Incrementally Better Cookies 提供了兩項關鍵改進:

  • 那麼沒有設置SameSite屬性的cookie將等同設置了SameSite=Lax
  • cookie設置SameSite=None必須同時設置Secure,這意味着需要在更安全的環境下傳輸。

Chrome在84版本實現了以上默認行為。Firefox在69版本可以測試使用,並且未來會加入默認配置中。為了在Firefox中測試這些行為,打開about:config然後network.cookie.sameSite.laxByDefault。Edge也計劃改變其默認行為。

原文

//web.dev/samesite-cookies-explained/

Tags: