cookie 簡介

admin發表於2019-10-20

導讀

思維導圖

一、 簡介

首先要知道標準的 http 協議是無狀態的,也就是指服務端對於客戶端的每次請求都認為它是一個新的請求,上一次會話和下一次會話之間是沒有任何聯絡,這時當使用者訪問服務端並進行登入後,客戶端之後的請求服務端依然無法對客戶端身份進行識別, 如果將客戶端與伺服器之間的多次互動當做一個整體來看,那麼服務端若想識別客戶端的身份那麼就需要將多次互動所涉及的資料(狀態)儲存下來。 cookie 的作用就是對請求和響應的狀態進行一個管理, 服務端通過在響應體中設定 cookie (狀態), 客戶端會將 cookie (狀態) 儲存起來, 之後客戶端的每個請求都將 cookie (狀態)帶上, 這樣服務端就能夠對客戶端的身份進行識別;在早期 cookie 還未出現之前有個最簡單的辦法就是在請求的頁面中插入一個 token,然後在下次請求時將這個 token 返回至伺服器。這需要在頁面的 form 表單中插入一個包含 token 的隱藏域,或者將 token 放在 URL 的 query 字串中來傳遞。這兩種方法都需要手動操作,而且極易出錯, 而 cookie 則不同一般情況下客戶端會自動儲存伺服器發來的 cookie, 並在之後的每次請求中自動帶上 cookie 而無需客戶端進行手動處理。

補充: 標準的http協議指的是不包括cookies, session,application的http協議,他們都不屬於標準協議,雖然各種網路應用提供商,實現語言、web容器等,都預設支援它。

二、 基本原理

原理圖

當瀏覽器向伺服器傳送請求的時候,伺服器會將少量的資料以 set-cookie 訊息頭的方式傳送給客戶端, 瀏覽器一般會自動將 cookie 資料進行儲存,當客戶端再次訪問伺服器時,會將這些資料以 cookie 訊息頭的方式傳送給伺服器,服務端就可以根據 cookie 訊息頭來判別使用者的身份或進行一些特別的處理並返回響應。

三、 生存期

從 cookie 的工作原理上可以知道 cookie 資訊主要是儲存在客戶端的狀態, 而 cookie 在客戶端的生存期則主要由屬性 max-age 決定, max-age 屬性以秒為單位表示 cookie 的存活時間為 max-age 秒; 當然 cookie 還有一個屬性 expires 其作用和 max-age 是一樣的這在後續會進行詳細說明。

  • 預設情況下 cookie 只是暫時存在的,他們儲存的值只在瀏覽器會話期間存在,當使用者關閉瀏覽器視窗後這些值也會隨之銷燬(生成的 cookie 臨時儲存於瀏覽器記憶體中)
  • max-age 為正數: cookie 會在 max-age 秒之後被銷燬(會將狀態儲存於 cookie 檔案中並儲存於本地硬碟中)
  • max-age 為負數時: cookie 只在瀏覽器會話期間存在,當使用者關閉瀏覽器視窗後這些值也會隨之銷燬(生成的 cookie 臨時儲存於瀏覽器記憶體中)
  • max-age 為 0 時: cookie 將被立即銷燬

四、 各大瀏覽器支援情況

IE 6 IE 7.0 8.0 Opera Fire Fox Safari   Chrome 
cookie 個數 每個域名下20個 每個域名下50個 每個域名30個 每個域名50個 沒有限制 每個域名53個
cookie 大小 4095位元組 4095位元組 4096位元組 4097位元組 4097位元組 4097位元組

五、 屬性介紹

上文說到 cookie 主要是用於管理服務端和客戶端直接的狀態, 其本質上就是一堆儲存在客戶端的資料, 每條 cookie 都有對應下面幾個屬性:

5.1 domain 和 path 屬性

domain 指定了該 cookie 所屬的域名,預設情況下,domain 會被設定為建立該 cookie 時所在的域名; 而 path 則指定了該 cookie 所屬的路徑; domain 和 path 兩者一起來限制了該 cookie 允許被哪些 URL 訪問, 當我們請求某個資源(URL)時只有當該 URL 域名能夠同時被 domain 和 path 屬性匹配時, 瀏覽器才會將此 cookie 新增到該請求的 cookie 頭部中。

domain 的匹配是根據請求 URL 中的域名從後向前進行匹配, path 的匹配則是根據 URL 中的路徑按照路徑的匹配規則判斷 URL 中的路徑是否包含 path, 例如:

domain 屬性 path 屬性 請求 URL 請求是否包含 cookie 描述
auth.com / b.auth.com/a/d -
b.auth.com /a b.auth.com/a/b -
b.auth.com /b b.auth.com/a/b path 屬性不匹配,路徑 /a/b 不包含 /b
b.auth.com /c b.auth.com/a/b path 屬性不匹配,路徑 /a/b 不包含 /c
a.auth.com / b.auth.com/a/b domain 不匹配

5.2 expires/max-age

首先需要知道清楚 expires 是 http/1.0 協議中的屬性,在新的 http/1.1 協議之後 expires 已經由 max-age 選項代替,兩者的作用都是限制 cookie 的有效時間。

expires 屬性指定一個具體的到期時間,到了指定時間以後,瀏覽器就不再保留這個 cookie 。它的值是 UTC 格式,可以使用 Date.prototype.toUTCString() 進行格式轉換。而 max-age 屬性則是指定從現在開始 cookie 存在的秒數,比如 60 * 60 * 24 * 365 (即一年)。過了這個時間以後,瀏覽器就不再保留這個 cookie 。如果同時指定了 expires 和 max-age,那麼 max-age 的值將優先生效。如果沒有指定 expires 或 max-age 屬性,那麼這個 cookie 就是 Session Cookie, 即它只在本次對話存在,一旦使用者關閉瀏覽器視窗, 瀏覽器將不會再保留這個 cookie 。

5.3 secure

該屬性只是一個標記而沒有值。包含 secure 選項的 cookie 只有在當請求是 HTTPS 或者其他安全協議時, 才能被髮送至伺服器。預設情況下, cookie 不會帶 secure 選項(即為空)。所以預設情況下,不管是 HTTPS 協議還是 HTTP 協議的請求,cookie 都會被髮送至服務端。但要注意一點,secure 選項只是限定了在安全情況下才可以傳輸給服務端,但並不代表你不能看到這個 cookie。同時需要注意的是, 如果想在客戶端即網頁中通過 js 去設定 secure 型別的 cookie,必須保證網頁是 https 協議的。在http協議的網頁中是無法設定 secure 型別 cookie 的。

5.4 httpOnly

httpOnly 背後的意思是告之瀏覽器該 cookie 絕不能通過 JavaScript 的 document.cookie 屬性訪問。設計該特徵意在提供一個安全措施來幫助阻止通過 JavaScript 發起的跨站指令碼攻擊 (XSS) 竊取 cookie 的行為, 一旦設定這個標記,通過 documen.coookie 則不能再訪問該 cookie。

5.5 sameSite

sameSite-cookies 是一種機制,用於定義 cookie 如何跨域傳送,其目的主要是為了嘗試阻止 CSRF (Cross-site request forgery 跨站請求偽造)以及 XSSI (Cross Site Script Inclusion (XSSI) 跨站指令碼包含)攻擊。

CSRF攻擊簡述: CSRF 攻擊主要是盜用使用者資訊併傳送惡意請求的一種攻擊方式, 比如說某使用者登入一個完全網站 A, A 站點返回一個能夠標識使用者身份的 cookie, 當使用者無意中訪問一個惡意網站 B, B 站點向 A 站點發起惡意請求, 這時請求中將會帶上具有使用者身份標識的 cookie。

XSSI攻擊簡述: XSSI 是 XSS 的一種形式, 假設網站 A 有一個指令碼用於讀取使用者的私人賬戶資訊, 攻擊者可以在自己的惡意網站包含這個指令碼, 當網站 A 的使用者訪問攻擊者的網站時該網站將載入該指令碼並帶上使用者身份標識 (cookie) 該指令碼將讀取使用者的私人賬戶資訊, 這時使用者的資訊可能就會發生洩露。

sameSite 屬性可能值: 為 cookie 設定 sameSite 屬性時需要給其定義一個值(如果沒有設定值,預設是Strict),值可以是 Lax 或者 Strict

strict: 當 sameSite 屬性值為 strict 時, 將禁止傳送所有第三方連結的 cookies, 比如有 cookie ( domain = a.com, sameSite = strict ), 那麼在其他網站請求 a.com 時都不會帶上該 cookie

lax: 當 sameSite 屬性值為 lax 時, 只會在使用危險 HTTP 方法傳送跨域 cookie 的時候進行阻止,例如 POST 方式。比如有 cookie ( domain = a.com, sameSite = lax ), 那麼在其他網站通過 POST 方式請求 a.com 時都不會帶上該 cookie

六、 前端操作 cookie

6.1 建立 cookie

前端通過 document.cookie 設定 cookie, 設定多個 cookie 則需要編寫多條 document.cookie = '...', 如果需要對 cookie 進行修改建立一條相同名稱的 cookie 即可對 cookie 進行替換

// 建立多條 cookie 並設定 path 和 domain 以及 max-age 屬性
document.cookie = 'name1=value1;path=/;domain=localhost;max-age=30';
document.cookie = 'name2=value2;path=/;domain=localhost;max-age=30';
document.cookie = 'name3=value3;path=/;domain=localhost;max-age=30';

/**
 * 通用方法: 設定 cookie 方法
 * @param {String} name     cookie 名稱
 * @param {String} value    cookie 值
 * @param {Number} maxAge   cookie 存活時間(maxAge秒, 預設儲存 30 天)
 */
function setCookie({name, value, maxAge}) {
  maxAge = maxAge || 30*24*60*60*1000;
  // escape: 對字串進行編碼
  document.cookie = `${name}=${escape (value)};max-age=${maxAge}`;
}
複製程式碼

通過瀏覽器(chrome)控制檯檢視 cookie 資訊

cookie資訊

6.2 獲取 cookie

前端可直接通過 document.cookie 獲取 cookie

// 列印 cookie
console.log(document.cookie); // name1=value1; name2=value2; name3=value3

/**
 * 通用方法: 將 cookie 轉為物件 {anme: value}
 * @return {Object} {name, value}
 */
function getCookieObj() {
  let cookieArr = document.cookie.split(";");
  let obj = {};
  cookieArr.forEach( v => {
    let arr = v.split("=");
    obj[arr[0]] =  unescape(arr[1]);
  });
  return obj
}
複製程式碼

6.3 刪除 cookie

前端並沒有直接的 api 可以直接實現對 cookie 的刪除, 可以通過將 cookie 的 max-age 屬性設定 0 來實現對 cookie 的刪除

// 刪除 cookie
document.cookie = 'name3=value3;max-age=0';

/**
 * 通用方法: 刪除 cookie 方法
 * @param {String} name     cookie 名稱
 */
function delCookie({ name }) {
  document.cookie = `${name}=;max-age=0`;
}
複製程式碼

七、 服務端操作 cookie

7.1 設定 cookie

服務端設定 cookie 主要是通過設定響應頭 set-cookie 來設定 cookie, 如果需要設定多個 cookie 得多寫幾個 set-cookie

// 1. koa 中直接通過設定請求頭的方式設定 cookie
// 2. 在 node 中 response 響應中設定兩次相同的 header 屬性會發生合併, 可以通過一個陣列來實現
ctx.set(
  'Set-cookie',
  'name=value;path=/user;domain=localhost;max-age=30;HttpOnly;SameSite=Strict'
);
複製程式碼

檢視響應頭

響應頭

檢視瀏覽器控制檯

控制檯

7.2 獲取 cookie

伺服器獲取 cookie 實際上就是獲取請求頭 cookie

// koa 中獲取請求頭資訊並進行列印(獲取的 cookie 格式和前端獲取到的格式是一樣的所以也就可以通過相同的方式來處理)
console.log(ctx.request.headers.cookie); // name1=value1; name2=value2; name3=value3
複製程式碼

檢視請求頭資訊

請求頭

相關文章