一文解讀Cookie


什么是Cookie?

Cookie 就是訪問者在訪問網站后留下的一個信息片段。它存儲在客戶端(通常來說是瀏覽器)。你可以把cookie當作一個map,里邊是鍵值對,每個鍵值對有過期時間路徑腳本可否訪問等描述信息;描述信息存儲在客戶端,客戶端請求時,默認會帶上cookie的名稱和值,不會帶描述信息,通過http請求報文header中的cookie項進行傳輸;服務器響應時,可以設置cookie信息,就在http響應報文的headerSet-Cookie項。

那么,它究竟有什么作用呢?

眾所周知,HTTP 協議是無狀態的協議,如果你在同一個客戶端向服務器發送多次請求,服務器不會知道這些請求來自同一客戶端(同一個用戶)。

這樣有什么好處呢?如果它是有狀態協議,你必須要時刻與服務器建立鏈接,那么如果連接意外斷開,整個會話就會丟失,重新連接之后一般需要從頭開始;而如果是無狀態協議,使得會話與連接本身獨立起來,這樣即使連接斷開了,會話狀態也不會受到嚴重傷害,保持會話也不需要保持連接本身。

但是,缺點也很明顯:即使同一個客戶端連續兩次發送請求給服務器,服務器也識別不出這是同一個客戶端發送的請求,這導致的問題就比如你加了一個商品到購物車中,但因為識別不出是同一個客戶端,你刷新下頁面就沒有了。

為了使服務器知道每個請求具體來自於哪個用戶,比如你在逛淘寶的時候你只需要登錄一次,當你發起一次購買請求,服務器就已經知道你登錄過了,不會再讓你進行登錄。

由此,Cookie誕生了,Cookie就是一種瀏覽器管理狀態的一個文件,讓無狀態的 HTTP 協議擁有一小塊記憶。

HTTP Cookie 機制是 HTTP 協議無狀態的一種補充和改良

Cookie 主要有以下用途:

  • 會話管理:登陸、購物車等應該記住的其他內容
  • 個性化:用戶偏好、主題或者其他設置

  • 追蹤:記錄和分析用戶行為

Cookie原理

第一次訪問網站的時候,瀏覽器發出請求,服務器響應請求后,會將cookie放入到響應請求中(就在http響應報文的headerSet-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)外過期。例如:

在這里插入圖片描述

name

cookie的名字,一個域名下綁定的cookiename不能相同,相同的name的值會被覆蓋掉

value

value表示cookie的值

【注】用 JavaScript操作 Cookie 的時候注意對 value 進行編碼處理。

Domain

這個代表的是,cookie綁定的域名,如果沒有設置,就會自動綁定到執行語句的當前域。由於同源策略,腳本只能訪問父域名或本域名的cookie(瀏覽器只能發送父域名或本域名的cookie),比如設置cookie域名為一級域名mydomain.com;那么此域名下的二級域名www.mydomain.comimages.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 不發送

設置了StrictLax以后,基本就杜絕了 CSRF 攻擊。當然,前提是用戶瀏覽器支持 SameSite 屬性。

None

Chrome 已將Lax變為默認設置。這時,網站可以選擇顯式關閉SameSite屬性,將其設為None。不過,前提是必須同時設置Secure屬性(Cookie只能通過 https 協議發送),否則無效。

Cookie的讀寫

document.cookie屬性用於讀寫當前網頁的 Cookie

讀取的時候,它會返回當前網頁的所有 Cookie,前提是該 Cookie 不能有HTTPOnly屬性。

document.cookie // "foo=bar;baz=bar"

document.cookie屬性是可寫的,可以通過它為當前網站添加 Cookie
寫入的時候,Cookie的值必須寫成key=value的形式。注意,等號兩邊不能有空格。另外,寫入 Cookie 的時候,必須對分號、逗號和空格進行轉義(它們都不允許作為 Cookie 的值),這可以用encodeURIComponent方法達到。
但是,document.cookie一次只能寫入一個 Cookie,而且寫入並不是覆蓋,而是添加。

document.cookie = 'test1=hello'; document.cookie = 'test2=world'; document.cookie // test1=hello;test2=world

document.cookie讀寫行為的差異(一次可以讀出全部 Cookie,但是只能寫入一個 Cookie),與 HTTP 協議的 Cookie 通信格式有關。瀏覽器向服務器發送 Cookie 的時候,Cookie字段是使用一行將所有 Cookie 全部發送;服務器向瀏覽器設置 Cookie 的時候,Set-Cookie字段是一行設置一個 Cookie

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

document.cookie = "test=hello; expires=Fri, 31 Dec 2020 23:59:59 GMT";

上面代碼中,寫入 Cookie 的時候,同時設置了expires屬性。屬性值的等號兩邊,也是不能有空格的

各個屬性的寫入注意點如下。

  • path屬性必須為絕對路徑,默認為當前路徑。
  • domain屬性值必須是當前發送 Cookie 的域名的一部分。比如,當前域名是example.com,就不能將其設為foo.com。該屬性默認為當前的一級域名(不含二級域名)。
  • max-age屬性的值為秒數。
  • expires屬性的值為 UTC 格式,可以使用Date.prototype.toUTCString()進行日期格式轉換。

document.cookie寫入 Cookie 的例子如下。

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

Cookie 的屬性一旦設置完成,就沒有辦法讀取這些屬性的值。

刪除一個現存 Cookie的唯一方法,是設置它的expires屬性為一個過去的日期。或者上面提到的Max-Age屬性。

document.cookie = 'fontSize=;expires=Thu, 01-Jan-1970 00:00:01 GMT';

上面代碼中,名為fontSize的 Cookie 的值為空,過期時間設為1970年1月1月零點,就等同於刪除了這個 Cookie


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM