完整閱讀本文大約須要二十分鐘時間,可根據文章結構圖直接閱讀本身須要的部分。javascript
全部新技術的出現都是爲了解決某一痛點。 ——《前端三昧》html
咱們都知道,HTTP 協議
是無狀態的,服務器沒法知道兩個請求是否來自同一個瀏覽器,也不知道用戶上一次作了什麼,每次請求都是徹底相互獨立,這嚴重阻礙了交互式 Web
應用程序的實現。例子:前端
HTTP
的無狀態性,不經過額外的手段,服務器並不知道用戶到底買了什麼。Cookie
記住了登陸憑據,想要實現該功能將會很複雜。正是爲了解決這些交互方面存在的痛點,Cookie
應運而生。java
Cookie
( 也叫Web Cookie
或瀏覽器 Cookie
)是服務器發送到用戶瀏覽器並保存在本地的一小塊數據,它會在瀏覽器下次向同一服務器再發起請求時被攜帶併發送到服務器上。git
存儲 Cookie
是瀏覽器提供的功能。Cookie
實際上是存儲在瀏覽器中的純文本,瀏覽器的安裝目錄下會專門有一個 Cookie
文件夾來存放各個域下設置的 Cookie
(非內存 Cookie
)。數據庫
一般,它用於告知服務端兩個請求是否來自同一瀏覽器,或者用來保存一些狀態信息,Cookie
使基於無狀態的 HTTP
協議記錄穩定的狀態信息成爲了可能。經常使用的有如下方面:跨域
session
)管理:保存登陸、購物車等須要記錄的信息。
Cookie
主要是用來存儲狀態的。瀏覽器
Cookie
曾一度用於客戶端數據的存儲,因當時並無其它合適的存儲辦法而做爲惟一的存儲手段。如今來講,這樣作雖然可行,可是並不推薦,由於 Cookie
的設計目標並非這個,它:緩存
客戶端儲存應該更多的考慮使用 localStorage
、sesseionStorage
和 IndexedDB
。安全
查看瀏覽器上存儲的 Cookie
的方法以下圖:
固然,瀏覽器能夠設置不接受 Cookie
,也能夠設置不向服務器發送 Cookie
。window.navigator.cookieEnabled
屬性返回一個布爾值,表示瀏覽器是否打開 Cookie
功能。
// 瀏覽器是否打開 Cookie 功能
window.navigator.cookieEnabled // true
複製代碼
本文全部的討論都是在瀏覽器的
window.navigator.cookieEnabled
爲 true 的前提下進行的。
Cookie
的工做流程Cookie
只能存儲純文本格式,由於:
Cookie
的大小有限制Cookie
中存儲的是不可執行語句因爲 Cookie
是保存在客戶端上的,因此瀏覽器加入了一些限制確保 Cookie
不會被惡意使用,同時不會佔據太多磁盤空間,因此 Cookie
的數量和大小是有限的。
不一樣瀏覽器對 Cookie
數量和大小的限制,是不同的。通常來講,單個域設置的 Cookie
不該超過 50個,每一個 Cookie 的大小不能超過 4KB 。超過限制之後,Cookie
將被忽略,不會被設置。
其限制的緣由,主要在於阻止
Cookie
的濫用,並且Cookie
會被髮送到服務器端,若是數量太大的話,會嚴重影響請求的性能。以上這兩個限制條件,就是Cookie
爲何會被瀏覽器自動刪除的緣由了。
不可跨域讀取,Cookie
是被哪一個域寫入的,就只能被這個域及其子域讀取。好比:
由 test.com
寫入的 Cookie
能夠被 test.com
和 test.com/child
讀取,而不能被 example.com
讀取。
存儲 Cookie
時會指定路徑,該路徑的子級能夠讀取該 Cookie
,可是它的父級卻讀取不到——子能夠讀取父,但父不能拿到子,例如:
由 test.com/parent/child
存儲下的 Cookie
,能夠被 test.com/parent/child/child
讀取,但不能被 test.com/parent
讀取。
通常會將
Cookie
存在根路徑下,能夠避免這種狀況的發生。
每一個 Cookie
都有時效性,默認的有效期是會話級別( Seesion Cookie
):就是當瀏覽器關閉,那麼 Cookie
當即銷燬,可是咱們也能夠在存儲的時候手動設置 Cookie
的過時時間,具體設置方法會在下文講到。
設置 Cookie
的名稱及相對應的值,對於認證 Cookie
,Value
值包括 Web
服務器所提供的訪問令牌 。
指定了能夠訪問該 Cookie
的 Web 站點或域。
Cookie
機制並未遵循嚴格的同源策略,容許一個子域能夠設置或獲取其父域的 Cookie
。
當須要實現單點登陸方案時,Cookie
的上述特性很是有用,然而也增長了 Cookie
受攻擊的危險,好比攻擊者能夠藉此發動會話定置攻擊。於是,瀏覽器禁止在 Domain
屬性中設置 .org、.com 等通用頂級域名、以及在國家及地區頂級域下注冊的二級域名,以減少攻擊發生的範圍。
Path
標識指定了主機下的哪些路徑能夠接受 Cookie
(該 URL 路徑必須存在於請求 URL 中)。以字符 %x2F ("/")
做爲路徑分隔符,子路徑也會被匹配。
設置 Cookie
的生存期。有兩種存儲類型的 Cookie
:會話性與持久性。
Expires
屬性指定一個具體的到期時間,到了這個指定的時間以後,瀏覽器就再也不保留這個 Cookie
,它的值是 UTC 格式,可使用 Date.prototype.toUTCString()
格式進行轉換。
Max-Age
屬性制定了從如今開始 Cookie
存在的秒數,好比 60 * 60 * 24 * 365(即一年)。過了這個時間之後,瀏覽器就再也不保留這個 Cookie
若是沒有設置這兩個選項,則會使用默認值。
domain
的默認值爲設置該Cookie
的網頁所在的域名,path
默認值爲設置該Cookie
的網頁所在的目錄。
Expires
屬性缺省時,爲會話性 Cookie(Session Cookie)
,僅保存在客戶端內存中,並在用戶關閉瀏覽器時失效。Cookie
會保存在用戶的硬盤中,直至生存期到或用戶直接在網頁中單擊「註銷」等按鈕結束會話時纔會失效。當
Cookie
的過時時間被設定時,設定的日期和時間只與客戶端相關,而不是服務端。
這個選項用來設置 Cookie
是否能經過 JavaScript
去訪問。默認狀況下, Cookie
不會帶 HTTPOnly
選項(即爲空),因此默認狀況下,客戶端是能夠經過 JavaScript
代碼去訪問(包括讀取、修改、刪除等)這個 Cookie
的。當 Cookie
帶 HTTPOnly
選項時,客戶端則沒法經過js代碼去訪問(包括讀取、修改、刪除等)這個 Cookie
。
用於防止客戶端腳本經過 document.cookie
屬性訪問 Cookie
,有助於保護 Cookie
不被跨站腳本攻擊竊取或篡改。可是,HTTPOnly
的應用仍存在侷限性,一些瀏覽器能夠阻止客戶端腳本對 Cookie
的讀操做,但容許寫操做;此外大多數瀏覽器仍容許經過 XMLHTTP
對象讀取 HTTP
響應中的 Set-Cookie
頭 。
在客戶端是不能經過
JAvaScript
代碼去設置一個httpOnly
類型的Cookie
的,這種類型的Cookie
只能經過服務端來設置。
指定是否使用 HTTPS
安全協議發送 Cookie
。
使用 HTTPS
安全協議,能夠保護 Cookie
在瀏覽器和 Web
服務器間的傳輸過程當中不被竊取和篡改。該方法也可用於 Web
站點的身份鑑別,即在 HTTPS
的鏈接創建階段,瀏覽器會檢查 Web
網站的 SSL
證書的有效性。
可是基於兼容性的緣由(好比有些網站使用自簽署的證書)在檢測到 SSL
證書無效時,瀏覽器並不會當即終止用戶的鏈接請求,而是顯示安全風險信息,用戶仍能夠選擇繼續訪問該站點。因爲許多用戶缺少安全意識,於是仍可能鏈接到 Pharming
攻擊所僞造的網站 。
若是當前協議是 HTTP,瀏覽器會自動忽略服務器發來的 Secure。
Cookie
容許服務器要求某個 Cookie
在跨站請求時不會被髮送,(其中 Site
由可註冊域定義),從而能夠阻止跨站請求僞造攻擊(CSRF
)。
SameSite cookies
是相對較新的一個字段,全部主流瀏覽器都已經獲得支持。下面是例子:
Set-Cookie: key=value; SameSite=Strict
複製代碼
SameSite
能夠有下面三種值:
None
瀏覽器會在同站請求、跨站請求下繼續發送 Cookies
,不區分大小寫。Strict
瀏覽器將只在訪問相同站點時發送 Cookie
。(在原有 Cookies
的限制條件上的增強)。Lax
與 Strict
相似,但用戶從外部站點導航至URL時(例如經過連接)除外。 在新版本瀏覽器中,爲默認選項,Same-site cookies
將會爲一些跨站子請求保留,如圖片加載或者 frames
的調用,但只有當用戶從外部站點導航到 URL
時纔會發送。如 link 連接。之前,若是
SameSite
屬性沒有設置,或者沒有獲得運行瀏覽器的支持,那麼它的行爲等同於None
,Cookies
會被包含在任何請求中——包括跨站請求。大多數主流瀏覽器正在將
SameSite
的默認值遷移至Lax
。若是想要指定Cookies
在同站、跨站請求都被髮送,如今須要明確指定SameSite
爲None
。
Cookie
機制的使得服務器沒法確認 Cookie
是在安全來源上設置的,甚至沒法肯定 Cookie
最初是在哪裏設置的。
子域上的易受攻擊的應用程序可使用 Domain
屬性設置 Cookie
,從而能夠訪問全部其餘子域上的該 Cookie
。會話定置攻擊中可能會濫用此機制。
可是,做爲 深度防護措施
,可使用 Cookie
前綴來斷言有關 Cookie
的特定事實。有兩個前綴可用:
__Host-
若是 Cookie
名稱具備此前綴,則僅當它也用 Secure
屬性標記,是從安全來源發送的,不包括 Domain
屬性,並將 Path
屬性設置爲 /
時,它纔在 Set-Cookie
標頭中接受。這樣,這些 Cookie
能夠被視爲 "domain-locked
」。
__Secure-
若是 Cookie
名稱具備此前綴,則僅當它也用 Secure
屬性標記,是從安全來源發送的,它纔在 Set-Cookie
標頭中接受。該前綴限制要弱於 __Host-
前綴。
帶有這些前綴點 Cookie
, 若是不符合其限制的會被瀏覽器拒絕。請注意,這確保了若是子域要建立帶有前綴的 Cookie
,那麼它將要麼侷限於該子域,要麼被徹底忽略。因爲應用服務器僅在肯定用戶是否已經過身份驗證或 CSRF 令牌正確時才檢查特定的 Cookie
名稱,所以,這有效地充當了針對會話劫持的防護措施。
Cookie
各個屬性的兼容性以下圖所示:
服務器若是但願在瀏覽器保存 Cookie
,就要在 HTTP
迴應的頭信息裏面,放置一個Set-Cookie
字段。
瀏覽器收到響應後一般會保存下 Cookie
,以後對該服務器每一次請求中都經過 Cookie
請求頭部將 Cookie
信息發送給服務器。另外,Cookie
的過時時間、域、路徑、有效期、適用站點均可以根據須要來指定。
HTTP
迴應能夠包含多個 Set-Cookie
字段,即在瀏覽器生成多個 Cookie
。下面是一個例子。
HTTP/1.0 200 OK
Content-type: text/html
Set-Cookie: yummy_cookie=choco
Set-Cookie: tasty_cookie=strawberry
[page content]
複製代碼
除了 Cookie
的值,Set-Cookie
字段還能夠附加 Cookie
的屬性。
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
字段裏面,能夠同時包括多個屬性,沒有次序的要求。
若是服務器想改變一個早先設置的
Cookie
,必須同時知足四個條件:Cookie
的key
、domain
、path
和secure
都匹配。不然,會建立一個新的Cookie
。
瀏覽器接收了響應頭提供的 Cookie
以後,每一次訪問該域時,都會攜帶該 Cookie
值:
Cookie
字段能夠包含多個 Cookie,使用分號(;
)分隔。
GET /sample_page.html HTTP/1.1
Host: www.example.org
Cookie: yummy_cookie=choco; tasty_cookie=strawberry
複製代碼
經過 document.cookie
屬性可建立新的 Cookie
,也可經過該屬性訪問非 HttpOnly 標記的 Cookie
。
上圖從 document.cookie
一次性讀出多個 Cookie
,它們之間使用分號分隔。必須手動還原,才能取出每個 Cookie
的值。
寫入的時候,Cookie
的值必須寫成 key=value
的形式。注意,等號兩邊不能有空格。另外,寫入 Cookie
的時候,必須對分號、逗號和空格進行轉義(它們都不容許做爲 Cookie
的值),這能夠用 encodeURIComponent
方法達到。好比,咱們要存儲一個對象到 Cookie
中,能夠經過下面代碼實現:
設置完成後,在瀏覽器查看:
那要怎麼才能讀取到此次設置的 Cookie
呢?方法以下:
讀取到的結果以下:
document.cookie
一次只能寫入一個Cookie
,並且寫入並非覆蓋,而是添加。
信息被存在
Cookie
中時,須要明白Cookie
的值時能夠被訪問,且能夠被終端用戶所修改的。根據應用程序的不一樣,可能須要使用服務器查找的不透明標識符,或者研究諸如JSON Web Tokens
之類的替代身份驗證/機密機制。當機器處於不安全環境時,切記不能經過
HTTP Cookie
存儲、傳輸敏感信息。
攻擊者能夠經過木馬等惡意程序,或使用跨站腳本攻擊等手段偷竊存放在用戶硬盤或內存中的 Cookie
。藉助網絡攻擊手段,包括:
DNSPharming
(域欺騙)攻擊,經過 DNS 緩存中毒
、DNS 應答欺騙
、或修改用戶端的本地域名解析文件等方法攻擊 DNS
系統,致使用戶對合法網站的訪問請求被重定向到惡意網站等等,一樣可能竊取 Cookie
。對於捕獲到的認證 Cookie
,攻擊者每每會猜想其中的訪問令牌,試圖獲取會話ID、用戶名與口令、用戶角色、時間戳等敏感信息;或者直接重放該 Cookie
,假冒受害者的身份發動攻擊 。
Cookies
是文本文件, 通常狀況下認爲它不會形成安全威脅。 可是,若是在 Cookies
中經過特殊標記語言,引入可執行代碼,就極可能給用戶形成嚴重的安全隱患。HTML
爲區別普通文本和標記語言,用符號 「<>」
來指示 HTML
代碼。 這些 HTML
代碼或者定義 Web
網頁格式,或者引入 Web
瀏覽器可執行代碼段。 Web
服務 器可使用 Cookies
信息建立動態網頁。假使 Cookies
包含可執行惡意代碼段,那麼在顯示合成有該 Cookies
的網頁時,就會自動執行這段惡意代碼。固然,惡意代碼可否真正形成危害,還取決於 Web
站點的安全配置策略 。
會話定置(Session Fixation
)攻擊是指,攻擊者向受害者主機注入本身控制的認證 Cookie
等信息,使得受害者以攻擊者的身份登陸網站,從而竊取受害者的會話信息。
注入 Cookie
的方法包括:
跨站請求僞造(Cross-Site Request Forgery
,簡稱CSRF
)是指:
攻擊者可能利用網頁中的惡意代碼強迫受害者瀏覽器向被攻擊的 Web
站點發送僞造的請求,篡奪受害者的認證 Cookie
等身份信息,從而假冒受害者對目標站點執行指定的操做。
Firefox、Opera 等瀏覽器使用單進程機制,多個窗口或標籤使用同一個進程,共享 Cookie
等會話數據。IE 則混合使用單進程與多進程模式,一個窗口中的多個標籤,以及使用 「CTRL+N」 或單擊網頁中的連接打開的新窗口使用同一進程,共享會話數據;只有直接運行IE可執行程序打開窗口時,纔會建立新的進程。Chrome 雖然使用多進程機制,然而經測試發現,其不一樣的窗口或標籤之間仍會共享會話數據,除非使用隱身訪問方式。
於是,用戶同時打開多個瀏覽器窗口或標籤訪問互聯網資源時,就爲 CSRF
攻擊篡奪用戶的會話 Cookie
創造了條件。另外,若是一個Web 站點提供持久化 Cookie
,則 CSRF
攻擊將更直接、更容易。
緩解 Cookie 攻擊的方法以下:
- 對用戶輸入進行過濾來阻止 XSS;
- 任何敏感操做都須要確認;
- 用於敏感信息的 Cookie 只能擁有較短的生命週期;
有兩種方法能夠確保 Cookie
被安全發送,而且不會被意外的參與者或腳本訪問:Secure
屬性和 HttpOnly
屬性。
標記爲 Secure
的 Cookie
只應經過被 HTTPS
協議加密過的請求發送給服務端,所以能夠預防 man-in-the-middle
攻擊者的攻擊。但即使設置了 Secure
標記,敏感信息也不該該經過 Cookie
傳輸,由於 Cookie
有其固有的不安全性,Secure
標記也沒法提供確實的安全保障, 例如,能夠訪問客戶端硬盤的人能夠讀取它。
JavaScript Document.cookie API
沒法訪問帶有 HttpOnly
屬性的 Cookie
;此類 Cookie
僅做用於服務器。例如,例如,持久化服務器端會話的 Cookie
不須要對 JavaScript
可用,而應具備 HttpOnly
屬性。此預防措施有助於緩解跨站點腳本(XSS
)攻擊。
因爲 Cookie
在使用上存在較多限制,近年來,隨着技術的發展成熟,出現了幾種可替代 Cookie
的方案,且已被大多數主流瀏覽器支持。
在瀏覽器中存儲數據的另外一種方法是 Web Storage API。window.sessionStorage
和 window.localStorage
屬性與持續時間中的會話和永久 Cookie
相對應,可是存儲限制比 Cookie
大,而且永遠不會發送到服務器。
可使用 IndexedDB API
或基於它構建的庫來存儲更多結構化的數據。
Web SQL
是一種利用數據庫進行數據存儲並利用 SQL 處理檢索任務的 API。
歡迎你們來到個人「山頭」,我是「前端三昧」的做者 隱逸王 —— 一個想要作山大王的男人!
願和你一塊兒領略前端三昧,發現前端之美!