Thanks to visit codestin.com
Credit goes to developer.mozilla.org

此頁面由社群從英文翻譯而來。了解更多並加入 MDN Web Docs 社群。

View in English Always switch to English

使用 HTTP Cookie

cookie(也稱為 Web Cookie 或瀏覽器 Cookie)是伺服器傳送給使用者網頁瀏覽器的一小段資料。瀏覽器可以儲存 Cookie、建立新的 Cookie、修改現有的 Cookie,並在之後的請求中將它們傳回給同一個伺服器。Cookie 讓網頁應用程式能夠儲存有限的資料並記住狀態訊息;根據預設,HTTP 協定是無狀態的

在本文中,我們將探討 Cookie 的主要用途、解釋使用 Cookie 的最佳實踐,並檢視其隱私和安全方面的影響。

通常,伺服器會使用 HTTP Cookie 的內容來判斷不同的請求是否來自同一個瀏覽器/使用者,然後視情況發出個人化或通用的回應。以下描述一個基本的使用者登入系統:

  1. 使用者將登入憑證傳送給伺服器,例如透過表單提交。
  2. 如果憑證正確,伺服器會更新 UI 以表示使用者已登入,並回應一個包含 session ID 的 Cookie,該 Cookie 會在瀏覽器上記錄其登入狀態。
  3. 稍後,使用者移動到同一個網站上的不同頁面。瀏覽器會將包含 session ID 的 Cookie 連同對應的請求一起傳送,以表示它仍然認為使用者已登入。
  4. 伺服器會檢查 session ID,如果仍然有效,則會傳送個人化版本的新頁面給使用者。如果無效,則會刪除 session ID,並向使用者顯示通用版本的頁面(或可能顯示「存取被拒」的訊息並要求再次登入)。

上述登入系統描述的視覺化表示

Cookie 主要用於三個目的:

  • 會話管理:使用者登入狀態、購物車內容、遊戲分數,或任何其他伺服器需要記住的使用者會話相關細節。
  • 個人化:使用者偏好,例如顯示語言和 UI 主題。
  • 追蹤:記錄和分析使用者行為。

資料儲存

在 Web 早期,當沒有其他選擇時,Cookie 被用於一般的用戶端資料儲存目的。現在建議使用現代的儲存 API,例如 Web Storage APIlocalStoragesessionStorage)和 IndexedDB

它們是為儲存而設計的,永遠不會將資料傳送到伺服器,並且沒有使用 Cookie 進行儲存的其他缺點:

  • 瀏覽器通常限制每個網域的 Cookie 數量上限(因瀏覽器而異,通常在數百個左右),以及每個 Cookie 的大小上限(通常為 4KB)。儲存 API 可以儲存更大量的資料。
  • Cookie 會隨著每個請求一起傳送,因此可能會降低效能(例如在緩慢的行動數據連線上),特別是當你設定了很多 Cookie 時。

備註:要查看儲存的 Cookie(以及網頁正在使用的其他儲存),你可以在 Firefox 開發者工具中使用儲存空間檢測器,或在 Chrome 開發者工具中使用應用程式面板

在收到 HTTP 請求後,伺服器可以在回應中傳送一個或多個 Set-Cookie 標頭,每個標頭都會設定一個獨立的 Cookie。設定 Cookie 的方式是指定一個鍵值對,如下所示:

http
Set-Cookie: <cookie-name>=<cookie-value>

以下 HTTP 回應指示接收的瀏覽器儲存一對 Cookie:

http
HTTP/2.0 200 OK
Content-Type: text/html
Set-Cookie: yummy_cookie=chocolate
Set-Cookie: tasty_cookie=strawberry

[頁面內容]

備註:了解如何在各種伺服器端語言/框架中使用 Set-Cookie 標頭:PHPNode.jsPythonRuby on Rails

當發出新請求時,瀏覽器通常會將先前為目前網域儲存的 Cookie,在 Cookie HTTP 標頭中傳回給伺服器:

http
GET /sample_page.html HTTP/2.0
Host: www.example.org
Cookie: yummy_cookie=chocolate; tasty_cookie=strawberry

你可以指定一個到期日期或時間段,在此之後 Cookie 應被刪除且不再傳送。根據建立 Cookie 時在 Set-Cookie 標頭中設定的屬性,它們可以是永久性會話性 Cookie:

  • 永久性 Cookie 會在 Expires 屬性指定的日期之後被刪除:

    http
    Set-Cookie: id=a3fWa; Expires=Thu, 31 Oct 2021 07:28:00 GMT;
    

    或在 Max-Age 屬性指定的時間段之後:

    http
    Set-Cookie: id=a3fWa; Max-Age=2592000
    

    備註:Expires 的可用時間比 Max-Age 長,但 Max-Age 較不易出錯,並且在兩者都設定時具有優先權。這背後的理由是,當你設定 Expires 的日期和時間時,它們是相對於設定 Cookie 的用戶端。如果伺服器的時間設定不同,可能會導致錯誤。

  • 會話性 Cookie——沒有 Max-AgeExpires 屬性的 Cookie——會在目前會話結束時被刪除。瀏覽器定義了「目前會話」何時結束,有些瀏覽器在重新啟動時會使用會話還原。這可能導致會話性 Cookie 無限期地持續存在。

    備註:如果你的網站對使用者進行身份驗證,它應該在使用者每次驗證時重新生成並重新傳送會話性 Cookie,即使是已經存在的 Cookie。這種方法有助於防止會話固定攻擊,在這種攻擊中,第三方可以重複使用使用者的會話。

有一些技術旨在在 Cookie 被刪除後重新建立它們。這些被稱為「殭屍」cookie。這些技術違反了使用者隱私和控制的原則,可能違反資料隱私法規,並可能使使用它們的網站面臨法律責任。

要透過 HTTP 更新 Cookie,伺服器可以傳送一個帶有現有 Cookie 名稱和新值的 Set-Cookie 標頭。例如:

http
Set-Cookie: id=new-value

你可能出於多種原因想要這樣做,例如,如果使用者更新了他們的偏好,而應用程式希望在用戶端資料中反映這些變更(你也可以使用用戶端儲存機制,如 Web Storage 來實現)。

在瀏覽器中,你可以使用 Document.cookie 屬性或非同步的 Cookie Store API 透過 JavaScript 建立新的 Cookie。請注意,以下所有範例都使用 Document.cookie,因為它是最廣泛支援/最成熟的選項。

js
document.Cookie = "yummy_cookie=chocolate";
document.Cookie = "tasty_cookie=strawberry";

你也可以存取現有的 Cookie 並為其設定新值,前提是它們沒有設定 HttpOnly 屬性(即在建立它的 Set-Cookie 標頭中):

js
console.log(document.cookie);
// 輸出「yummy_cookie=chocolate; tasty_cookie=strawberry」

document.Cookie = "yummy_cookie=blueberry";

console.log(document.cookie);
// 輸出「tasty_cookie=strawberry; yummy_cookie=blueberry」

請注意,出於安全目的,你在初始化請求時不能直接透過傳送更新的 Cookie 標頭來更改 Cookie 值,例如,透過 fetch()XMLHttpRequest。請注意,也有充分的理由不應允許 JavaScript 修改 Cookie——即在建立時設定 HttpOnly。有關更多詳細訊息,請參見安全性一節。

安全性

當你在 Cookie 中儲存訊息時,預設情況下,所有 Cookie 值對終端使用者都是可見的,並且可以被他們更改。你絕對不希望你的 Cookie 被濫用——例如被惡意行為者存取/修改,或被傳送到不應傳送的網域。潛在的後果可能從惱人的——應用程式無法運作或表現出奇怪的行為——到災難性的。例如,犯罪分子可以竊取一個 session ID,並用它來設定一個 Cookie,使其看起來像是以另一個人的身份登入,從而控制他們的銀行或電子商務帳戶。

你可以用多種方式保護你的 Cookie,本節將對此進行回顧。

你可以透過兩種方式確保 Cookie 安全傳送,且不會被非預期的各方或腳本存取:使用 Secure 屬性和 HttpOnly 屬性:

http
Set-Cookie: id=a3fWa; Expires=Thu, 21 Oct 2021 07:28:00 GMT; Secure; HttpOnly
  • 帶有 Secure 屬性的 Cookie 僅會透過 HTTPS 協定以加密請求傳送至伺服器。它永遠不會與不安全的 HTTP 一起傳送(localhost 除外),這意味著中間人攻擊者無法輕易存取它。不安全的網站(URL 中帶有 http:)無法設定帶有 Secure 屬性的 Cookie。但是,不要以為 Secure 可以防止對 Cookie 中敏感訊息的所有存取。例如,有權存取用戶端硬碟(或 JavaScript,如果未設定 HttpOnly 屬性)的人可以讀取和修改訊息。

  • 帶有 HttpOnly 屬性的 Cookie 不能被 JavaScript 存取,例如使用 Document.cookie;它只能在到達伺服器時被存取。例如,持久化使用者會話的 Cookie 應該設定 HttpOnly 屬性——讓它們對 JavaScript 可用會非常不安全。這項預防措施有助於減輕跨網站指令碼攻擊(XSS)。

備註:根據應用程式的不同,你可能希望使用一個由伺服器查詢的不透明識別碼,而不是直接在 Cookie 中儲存敏感訊息,或者研究替代的身份驗證/機密性機制,例如 JSON Web Token

DomainPath 屬性定義了 Cookie 的範圍:Cookie 被傳送到哪些 URL。

  • Domain 屬性指定哪個伺服器可以接收 Cookie。如果指定,Cookie 在指定的伺服器及其子網域上可用。例如,如果你從 mozilla.org 設定 Domain=mozilla.org,Cookie 在該網域和像 developer.mozilla.org 這樣的子網域上都可用。

    http
    Set-Cookie: id=a3fWa; Expires=Thu, 21 Oct 2021 07:28:00 GMT; Secure; HttpOnly; Domain=mozilla.org
    

    如果 Set-Cookie 標頭未指定 Domain 屬性,則 Cookie 在設定它的伺服器上可用,但不在其子網域上。因此,指定 Domain 比省略它限制更少。請注意,伺服器只能將 Domain 屬性設定為其自己的網域或父網域,而不能設定為子網域或其他網域。因此,例如,網域為 foo.example.com 的伺服器可以將屬性設定為 example.comfoo.example.com,但不能設定為 bar.foo.example.comelsewhere.com(不過 Cookie 仍然會被傳送到像 bar.foo.example.com 這樣的子網域)。有關更多詳細訊息,請參見無效網域

  • Path 屬性指示請求的 URL 中必須存在的 URL 路徑,以便傳送 Cookie 標頭。例如:

    http
    Set-Cookie: id=a3fWa; Expires=Thu, 21 Oct 2021 07:28:00 GMT; Secure; HttpOnly; Path=/docs
    

    %x2F(「/」)字元被視為目錄分隔符,子目錄也會匹配。例如,如果你設定 Path=/docs,這些請求路徑會匹配:

    • /docs
    • /docs/
    • /docs/Web/
    • /docs/Web/HTTP

    但這些請求路徑不會:

    • /
    • /docsets
    • /fr/docs

    備註:path 屬性讓你根據網站的不同部分來控制瀏覽器傳送哪些 Cookie。它不是作為安全措施,並且不能防止從不同路徑未經授權地讀取 Cookie。

SameSite 屬性讓伺服器可以指定是否/何時隨跨站請求傳送 Cookie——即第三方 Cookie。跨站請求是指網站(可註冊的網域)和/或協定(http 或 https)與使用者目前正在訪問的網站不匹配的請求。這包括在其他網站上點擊連結以導航到你的網站時傳送的請求,以及由嵌入的第三方內容傳送的任何請求。

SameSite 有助於防止訊息洩漏,保護使用者隱私並提供一些針對跨站請求偽造攻擊的保護。它有三個可能的值:StrictLaxNone

  • Strict 會使瀏覽器僅在回應源自 Cookie 原始網站的請求時才傳送 Cookie。當你有與功能相關的 Cookie,而這些功能總是在初始導航之後,例如身份驗證或儲存購物車訊息時,應該使用此設定。

    http
    Set-Cookie: cart=110045_77895_53420; SameSite=Strict
    

    備註:用於敏感訊息的 Cookie 也應該有較短的生命週期

  • Lax 與此類似,只是瀏覽器在使用者導航到 Cookie 的原始網站時也會傳送 Cookie(即使使用者來自不同的網站)。這對於影響網站顯示的 Cookie 很有用——例如,你的網站上可能有合作夥伴的產品訊息以及一個聯盟連結。當使用者跟隨該連結到合作夥伴網站時,他們可能希望設定一個 Cookie,說明該聯盟連結被跟隨,如果購買了產品,則會顯示獎勵橫幅並提供折扣。

    http
    Set-Cookie: affiliate=e4rt45dw; SameSite=Lax
    
  • None 指定 Cookie 在原始請求和跨站請求中都會被傳送。如果你想將 Cookie 與從嵌入在其他網站中的第三方內容發出的請求一起傳送,例如廣告技術或分析提供商,這就很有用。請注意,如果設定了 SameSite=None,則還必須設定 Secure 屬性——SameSite=None 需要一個安全上下文

    http
    Set-Cookie: widget_session=7yjgj57e4n3d; SameSite=None; Secure; HttpOnly
    

如果未設定 SameSite 屬性,則 Cookie 預設被視為 Lax

由於 Cookie 機制的設計,伺服器無法確認 Cookie 是否從安全來源設定,甚至無法知道 Cookie 最初是在哪裡設定的。

子網域上的應用程式可以設定帶有 Domain 屬性的 Cookie,這使得所有其他子網域都可以存取該 Cookie。這種機制可能在會話固定攻擊中被濫用。

然而,作為一種縱深防禦措施,你可以使用 Cookie 前綴來斷言關於 Cookie 的特定事實。有兩種前綴可用:

  • __Host-:如果 Cookie 名稱有這個前綴,它只有在同時標記了 Secure 屬性、從安全來源傳送、包含 Domain 屬性,並且 Path 屬性設定為 / 的情況下,才會在 Set-Cookie 標頭中被接受。換句話說,這個 Cookie 是網域鎖定的。
  • __Secure-:如果 Cookie 名稱有這個前綴,它只有在標記了 Secure 屬性並從安全來源傳送的情況下,才會在 Set-Cookie 標頭中被接受。這比 __Host- 前綴弱。

瀏覽器會拒絕不符合其限制的帶有這些前綴的 Cookie。這確保了由子網域建立的帶有前綴的 Cookie 要麼被限制在一個子網域內,要麼被完全忽略。由於應用程式伺服器在確定使用者是否已驗證或 CSRF 權杖是否正確時只檢查特定的 Cookie 名稱,這實際上起到了防禦會話固定攻擊的作用。

備註:在伺服器上,網頁應用程式必須檢查包含前綴的完整 Cookie 名稱。使用者代理在請求的 Cookie 標頭中傳送 Cookie 之前,不會從 Cookie 中剝離前綴。

有關 Cookie 前綴和目前瀏覽器支援狀態的更多訊息,請參見 Set-Cookie 參考文章的前綴部分

隱私與追蹤

前面我們談到如何使用 SameSite 屬性來控制何時傳送第三方 Cookie,以及這如何有助於保護使用者隱私。在建立網站時,隱私是一個非常重要的考量,如果做得好,可以與使用者建立信任。如果做得不好,則會完全侵蝕這種信任並導致各種其他問題。

第三方 Cookie 可以由透過 <iframe> 嵌入網站的第三方內容設定。它們有許多合法用途,包括共享使用者個人資料訊息、計算廣告曝光次數,或在不同相關網域之間收集分析資料。

然而,第三方 Cookie 也可用於建立令人毛骨悚然、侵入性的使用者體驗。第三方伺服器可以根據同一個瀏覽器在存取多個網站時傳送給它的 Cookie,來建立使用者瀏覽歷史和習慣的個人資料。典型的例子是,當你在一個網站上搜尋產品訊息後,無論你走到哪裡,網路上都會出現類似產品的廣告追著你跑。

瀏覽器供應商知道使用者不喜歡這種行為,因此都已開始預設阻擋第三方 Cookie,或至少已計劃朝這個方向發展。第三方 Cookie(或稱追蹤 Cookie)也可能被其他瀏覽器設定或擴充功能阻擋。

備註:阻擋 Cookie 可能會導致一些第三方組件(例如社群媒體小工具)無法按預期運作。隨著瀏覽器對第三方 Cookie 施加更多限制,開發者應該開始尋找減少對其依賴的方法。

有關第三方 Cookie、相關問題以及可用替代方案的詳細訊息,請參見我們的第三方 Cookie 文章。有關隱私的一般訊息,請參見我們的隱私著陸頁。

涵蓋 Cookie 使用的立法或法規包括:

這些法規具有全球影響力。它們適用於全球資訊網上任何使用者從這些司法管轄區(歐盟和加州)存取的網站(但加州法律僅適用於總收入超過 2500 萬美元的實體等情況)。

這些法規包括以下要求:

  • 通知使用者你的網站使用 Cookie。
  • 允許使用者選擇退出接收部分或全部 Cookie。
  • 允許使用者在不接收 Cookie 的情況下使用你的大部分服務。

你所在的地區可能還有其他管理 Cookie 使用的法規。你有責任了解並遵守這些法規。有些公司提供「Cookie 橫幅」程式碼,幫助你遵守這些法規。

備註:公司應出於透明度目的並為遵守法規,在其網站上披露他們使用的 Cookie 類型。例如,參見 Google 關於其使用的 Cookie 類型的通知和 Mozilla 的網站、通訊與 Cookie 隱私聲明

參見