一、保障策略
保障 Cookie 的安全性可以從以下幾個方面進行:
1.1 使用 HttpOnly 屬性
設定 HttpOnly 屬性可以防止JavaScript Document.cookie API 無法訪問帶有 HttpOnly 屬性的 cookie;此類 Cookie 僅作用於伺服器。例如,持久化伺服器端會話的 Cookie 不需要對 JavaScript 可用,而應具有 HttpOnly 屬性。此預防措施有助於緩解跨站點指令碼(XSS) (en-US)攻擊。
Set-Cookie: id=value; HttpOnly
1.2 使用 Secure 屬性
設定 Secure 屬性可以保證 Cookie 只在 HTTPS 連線中傳輸,防止在資料傳輸過程中被竊取。
Set-Cookie: id=value; Secure
1.3 設定 SameSite 屬性
SameSite 屬性可以防止跨站請求偽造(CSRF)攻擊。它有三個值:Strict、Lax 和 None,預設值為Lax。如果設定為 Strict,Cookie 將只在同一站點請求中傳送;如果設定為 Lax,Cookie 在導航到目標站點的 GET 請求中,或者在同一站點的請求中,Cookie 會被髮送;如果設定為 None,Cookie 會在所有請求中傳送,僅支援設定了 Secure 屬性的HTTPS站點。注意:各瀏覽器處理策略可能不同
Set-Cookie: id=value; SameSite=Strict
1.4 設定正確的過期時間
預設的cookie生命週期為臨時,僅會話期間存在,不應該設定過長的 Cookie 過期時間,以減少 Cookie 資訊被盜用的風險。
1.5 使用 Cookie 加密
對敏感資訊,如 session ID,進行加密後再儲存到 Cookie 中,即使 Cookie 資訊被竊取,攻擊者也無法獲得真正的內容。
1.6 限制 Cookie 的作用域
透過設定 Cookie 的 Domain 和 Path 屬性,限制 Cookie 的作用域,防止其在其他子域或路徑下被髮送。
Set-Cookie: id=value; Domain=example.com; Path=/blog
# 支援一級泛域名
Set-Cookie: id=value; Domain=.example.com
# 支援多個泛域名
Set-Cookie: id=value; Domain=.example1.com
Set-Cookie: id=value; Domain=.example2.com
以上都是保護 Cookie 安全的一些常見手段,但需要注意的是,這些措施並不能保證 100% 的安全,因為 Cookie 本質上是存在客戶端的,其安全性始終無法得到完全保障。在設計系統時,應儘量減少對 Cookie 的依賴,或者使用其他更安全的技術,如 Token、JWT 等。
二、注意點
2.1 SameSite 和 域(Domain)有啥區別?
SameSite
和 域(Domain)
是 Cookie 的兩個屬性,它們在 Cookie 的工作方式和安全性方面起著重要的作用。下面是它們各自的定義和作用:
- SameSite:
SameSite 是一個相對較新的 Cookie 屬性,用於防止跨站點請求偽造(CSRF)。它有三個可能的值:Strict
、Lax
和None
。
Strict
:Cookie 只有在同一個站點的請求中才會被髮送。Lax
:在導航到目標站點的 GET 請求中,或者在同一站點的請求中,Cookie 會被髮送。None
:Cookie 在所有請求中都會被髮送,無論是跨站點還是同站點。
- 域(Domain):
Domain 屬性定義了哪些網站可以接收 Cookie。如果沒有指定,那麼預設值就是建立 Cookie 的網頁所在的站點。
- 如果設定了 Domain,那麼所有的子域也都可以接收 Cookie。例如,如果設定了
Domain=example.com
,那麼sub.example.com
也可以接收 Cookie。 - 如果沒有設定 Domain,那麼只有建立 Cookie 的站點可以接收 Cookie。
所以,SameSite 和 Domain 在 Cookie 的安全性和可訪問性方面起著重要作用,但它們的工作方式是不同的。SameSite 控制 Cookie 是否可以在跨站點請求中傳送,而 Domain 控制哪些站點可以接收(讀) Cookie。
2.2 cookie的生命週期
Cookie 的生命週期主要由其設定的過期時間決定。以下是 Cookie 生命週期的一些基本資訊:
- 會話Cookie:如果不設定過期時間,或者設定的過期時間為0,那麼這個 Cookie 就是會話 Cookie。會話 Cookie 是臨時的,當使用者關閉瀏覽器時,這個 Cookie 就會被刪除。
Set-Cookie: id=a3fWa; Expires=0; Secure; HttpOnly
- 持久Cookie:如果在 Cookie 中設定了過期時間,那麼這個 Cookie 就是持久 Cookie。即使使用者關閉了瀏覽器,這個 Cookie 也會被保留,直到達到設定的過期時間,此後就會被瀏覽器刪除。
Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2025 07:28:00 GMT; Secure; HttpOnly
- 刪除Cookie:如果想要刪除 Cookie,可以透過設定過期時間為過去的某一時間來達到刪除的效果。
Set-Cookie: id=a3fWa; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Secure; HttpOnly
以上就是 Cookie 的生命週期的一些基本資訊。
2.3 XHR 請求中對 Cookie 的處理策略
對於同源請求,瀏覽器會預設帶上 cookie,無法透過設定 withCredentials=false
來取消
在進行 AJAX 域請求時,如果你不想帶上 cookie,你可以在建立 XMLHttpRequest 物件時,設定 withCredentials
屬性為 false
。這樣,瀏覽器在傳送 AJAX 請求時就不會帶上 cookie。
以下是一個簡單的示例:
var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://www.example.com/', true);
xhr.withCredentials = false; // 不帶 cookie,預設值即是false
xhr.send(null);
2.4 Fetch 請求中對 Cookie 的處理策略
fetch
API 中 credentials
的預設值是 "same-origin"
。
這意味著預設情況下,只有對同源的請求才會包含憑據(例如cookies,HTTP認證,客戶端SSL證明等)。如果你想在請求中包含憑據,即使對於跨源請求,你可以設定 credentials
的值為 "include"
。如果你不想在請求中包含憑據,你可以設定 credentials
的值為 "omit"
。
以下是使用 fetch
API 並設定 credentials
的示例:
fetch('https://example.com', {
credentials: 'include' // 包含憑據,即使是跨源請求
})
fetch('https://example.com', {
credentials: 'omit' // 不包含憑據
})
fetch('https://example.com', {
credentials: 'same-origin' // 只有對同源的請求才包含憑據
})
注意:對於CORS請求,瀏覽器會在請求中自動包含憑據,但伺服器必須指示瀏覽器返回響應,否則響應將被忽略。
2.5 Cookie 過大 可能引發的問題
瀏覽器層
每個瀏覽器對Cookie的大小都有限制,通常為4KB。如果Cookie的大小超過了這個限制,當 Cookie 的大小或數量超過瀏覽器的限制時,瀏覽器通常不會顯示一個明確的錯誤。而是會按照其自身的策略處理這種情況,這可能包括拒絕儲存新的 Cookie,刪除舊的 Cookie 來騰出空間,或者將大的 Cookie 截斷為小的 Cookie。
網路層
Cookie 是在每次 HTTP 請求中傳送的,如果 Cookie 過大,那麼網路傳輸的負載就會增大,這會影響網頁載入的速度和伺服器的響應速度。
服務端
Cookie 值過大,可能導致請求頭超過 Tomcat 和 Nginx 等web伺服器的大小限制,下面Tomcat 和 Nginx的配置資訊及處理策略:
1. Tomcat:
在 Tomcat 中,可以透過 maxHttpHeaderSize
屬性來限制 HTTP 請求頭的大小。預設值為 8192 位元組(8 KB)。你可以在 server.xml 檔案中的 Connector 標籤設定這個屬性。例如:
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
maxHttpHeaderSize="8192" />
如果請求頭的大小超過了 maxHttpHeaderSize
屬性的值,Tomcat 會返回 HTTP 400 錯誤。
2. Nginx:
在 Nginx 中,可以透過 large_client_header_buffers
指令來限制 HTTP 請求頭的大小。預設設定為 large_client_header_buffers 4 8k;
,意味著 Nginx 可以接受最多4個8KB的請求頭。
你可以在 nginx.conf 檔案中的 http 或 server 或 location 塊中設定這個指令。例如:
http {
large_client_header_buffers 4 16k;
}
如果請求頭的大小超過了 large_client_header_buffers
指令的值,Nginx 會返回 HTTP 414 錯誤(Request-URI Too Large)或者 HTTP 431 錯誤(Request Header Fields Too Large)。
三、總結與建議
- 預設情況下,Cookie 的生命週期為臨時會話級,而在最新的瀏覽器中,SameSite 的預設值設為 Lax,已具備較高安全性。根據實際需求或請求協議,可將 Cookie 設定為 HttpOnly 或 Secure,以進一步提升安全級別。
- 除非必要,否則不建議設定過期時間和作用域,以免無意間擴大 Cookie 的生命週期和作用範圍。
- 除非必要,否則不建議在 XHR 和 Fetch 請求中擴大 Cookie 的處理策略。