一文了解cookie
@
什麼是Cookie?
Cookie
就是訪問者在訪問網站後留下的一個資訊片段。它存儲在客戶端(通常來說是瀏覽器)。你可以把cookie
當作一個map
,裡邊是鍵值對,每個鍵值對有過期時間、域、路徑、腳本可否訪問等描述資訊;描述資訊存儲在客戶端,客戶端請求時,默認會帶上cookie
的名稱和值,不會帶描述資訊,通過http
請求報文header
中的cookie
項進行傳輸;伺服器響應時,可以設置cookie
資訊,就在http
響應報文的header
中Set-Cookie
項。
那麼,它究竟有什麼作用呢?
Cookie 的作用
眾所周知,HTTP
協議是無狀態的協議,如果你在同一個客戶端向伺服器發送多次請求,伺服器不會知道這些請求來自同一客戶端(同一個用戶)。
這樣有什麼好處呢?如果它是有狀態協議,你必須要時刻與伺服器建立鏈接,那麼如果連接意外斷開,整個會話就會丟失,重新連接之後一般需要從頭開始;而如果是無狀態協議,使得會話與連接本身獨立起來,這樣即使連接斷開了,會話狀態也不會受到嚴重傷害,保持會話也不需要保持連接本身。
但是,缺點也很明顯:即使同一個客戶端連續兩次發送請求給伺服器,伺服器也識別不出這是同一個客戶端發送的請求,這導致的問題就比如你加了一個商品到購物車中,但因為識別不出是同一個客戶端,你刷新下頁面就沒有了。
為了使伺服器知道每個請求具體來自於哪個用戶,比如你在逛淘寶的時候你只需要登錄一次,當你發起一次購買請求,伺服器就已經知道你登錄過了,不會再讓你進行登錄。
由此,Cookie
誕生了,Cookie
就是一種瀏覽器管理狀態的一個文件,讓無狀態的 HTTP
協議擁有一小塊記憶。
HTTP Cookie 機制是 HTTP 協議無狀態的一種補充和改良
Cookie
主要有以下用途:
會話管理
:登陸、購物車等應該記住的其他內容
-
個性化
:用戶偏好、主題或者其他設置 -
追蹤
:記錄和分析用戶行為
Cookie原理
第一次訪問網站的時候,瀏覽器發出請求,伺服器響應請求後,會將cookie
放入到響應請求中(就在http
響應報文的header
中Set-Cookie
項),在瀏覽器第二次發請求的時候,會把cookie
帶過去(http
請求報文header
中的cookie
項),服務端會辨別用戶身份,當然伺服器也可以修改cookie
內容。
Set-Cookie 和 Cookie 標頭
Set-Cookie
HTTP 響應標頭將 cookie 從伺服器發送到用戶代理。下面是一個發送 Cookie 的例子
隨著對伺服器的每個新請求,瀏覽器將使用 Cookie
頭將所有以前存儲的 Cookie
發送回伺服器。
Cookie的分類
有兩種類型的 Cookies
,一種是 Session Cookies
,一種是 Persistent Cookies
,如果 Cookie
不包含到期日期,則將其視為會話 Cookie
。會話 Cookie
存儲在記憶體中,永遠不會寫入磁碟,當瀏覽器關閉時,此後 Cookie
將永久丟失。如果 Cookie
包含有效期 ,則將其視為持久性 Cookie
。在到期指定的日期,Cookie
將從磁碟中刪除。還有一種是 Cookie
的 Secure
和 HttpOnly
標記,後面會介紹。
會話 Cookies
會話 Cookie
有個特徵,客戶端關閉時 Cookie
會刪除,因為它沒有指定Expires
或 Max-Age
指令。
但是,Web 瀏覽器可能會使用會話還原,這會使大多數會話 Cookie
保持永久狀態,就像從未關閉過瀏覽器一樣。
例如:
永久性 Cookies
永久性 Cookie
不會在客戶端關閉時過期,而是在特定日期(Expires)
或特定時間長度(Max-Age)
外過期。例如:
Cookie 的屬性
name
cookie
的名字,一個域名下綁定的cookie
,name
不能相同,相同的name
的值會被覆蓋掉
value
value
表示cookie
的值
【注】用 JavaScript
操作 Cookie
的時候注意對 value
進行編碼處理。
Domain
這個代表的是,cookie
綁定的域名,如果沒有設置,就會自動綁定到執行語句的當前域。由於同源策略,腳本只能訪問父域名或本域名的cookie
(瀏覽器只能發送父域名或本域名的cookie
),比如設置cookie
域名為一級域名mydomain.com
;那麼此域名下的二級域名www.mydomain.com
,images.mydomain.com
頁面,都可以訪問此cookie
【注】cookie
區分域,而不區分埠,也就是說,同一個ip
下的多個埠下的cookie
是共享的!更確切的說,請求是會帶上同域下所有埠的cookie
,但是返回時會覆蓋。
例如以下栗子:
Path
Path
這個屬性默認是'/'
,這個值匹配的是web
的路由
cookie
是區分路徑的,也就是說,同一個鍵值對可以同時設置到 www.mydomain.com,www.mydomain.com/b下,並且是獨立的,互不影響的;如果不指定路徑,默認是當前路徑。
Domain
和 Path
標識共同定義了 Cookie
的作用域:即 Cookie
應該發送給哪些 URL
。
Expires
Expires
用於設置 Cookie
的過期時間。當 Expires
屬性缺失時,表示是會話性 Cookie
,值保存在客戶端記憶體中,並在用戶關閉瀏覽器時失效。需要注意的是,有些瀏覽器提供了會話恢復功能,這種情況下即使關閉了瀏覽器,會話期 Cookie
也會被保留下來,就好像瀏覽器從來沒有關閉一樣。
與會話性 Cookie
相對的是持久性 Cookie
,持久性 Cookies
會保存在用戶的硬碟中,直至過期或者清除 Cookie
。這裡值得注意的是,設定的日期和時間只與客戶端相關,而不是服務端。
所以如果你想要cookie
存在一段時間,那麼你可以通過設置Expires
屬性為未來的一個時間節點,Expires
這個是代表當前時間的,然而這個屬性已經逐漸被Max-Age
代替。
Max-Age
Max-Age
用於設置在 Cookie
失效之前需要經過的秒數。
Max-Age
可以為正數、負數、甚至是 0。
-
當
Max-Age
屬性為正數時,瀏覽器會將其持久化,即寫到對應的Cookie
文件中。 -
當
Max-Age
屬性為負數,則表示該Cookie
只是一個會話性Cookie
。 -
當
Max-Age
為 0 時,則會立即刪除這個Cookie
。因為cookie
機制本身沒有設置刪除cookie
,失效的cookie
會被瀏覽器自動從記憶體中刪除,所以,它實現的就是讓cookie
失效。
【注】假如 Expires
和 Max-Age
都存在,Max-Age
優先順序更高。
Secure
由於http
不僅是無狀態的,還是不安全的協議,容易被劫持。所以標記為 Secure
的 Cookie
只應通過被https
協議加密過的請求發送給服務端。使用 https
安全協議,可以保護 Cookie
在瀏覽器和 Web
伺服器間的傳輸過程中不被竊取和篡改。
HTTPOnly
如果這個屬性設置為true
,就不能通過js
腳本來獲取cookie
的值,能有效的防止xss
攻擊。
SameSite
Chrome 51
開始,瀏覽器的 Cookie
新增加了一個SameSite
屬性,用來防止 CSRF
攻擊和用戶追蹤。
Cookie
的SameSite
屬性可以設置三個值:
- Strict
- Lax
- None
Strict
Strict
最為嚴格,完全禁止第三方 Cookie
,跨站點時,任何情況下都不會發送 Cookie
。換言之,只有當前網頁的 URL
與請求目標一致,才會帶上 Cookie
。
這個規則過於嚴格,可能造成非常不好的用戶體驗。比如,當前網頁有一個 GitHub
鏈接,用戶點擊跳轉就不會帶有 GitHub
的 Cookie
,跳轉過去總是未登陸狀態。
Lax
Lax
規則稍稍放寬,大多數情況也是不發送第三方 Cookie
,但是導航到目標網址的 Get
請求除外。
導航到目標網址的 GET 請求,只包括三種情況:鏈接,預載入請求,GET 表單。詳見下表。
請求類型 | 示例 | 正常情況 | Lax |
---|---|---|---|
鏈接 | <a href="..."></a> |
發送 Cookie | 發送 Cookie |
預載入 | <link rel="prerender" href="..."/> |
發送 Cookie | 發送 Cookie |
GET 表單 | <form method="GET" action="..."> |
發送 Cookie | 發送 Cookie |
POST 表單 | <form method="POST" action="..."> |
發送 Cookie | 不發送 |
iframe | <iframe src="..."></iframe> |
發送 Cookie | 不發送 |
AJAX | $.get("...") |
發送 Cookie | 不發送 |
Image | <img src="..."> |
發送 Cookie | 不發送 |
設置了Strict
或Lax
以後,基本就杜絕了 CSRF 攻擊。當然,前提是用戶瀏覽器支援 SameSite 屬性。
None
Chrome
已將Lax
變為默認設置。這時,網站可以選擇顯式關閉SameSite
屬性,將其設為None
。不過,前提是必須同時設置Secure
屬性(Cookie
只能通過 https
協議發送),否則無效。
參考: