如何保障Cookie的資訊保安

zengzuo613發表於2023-12-29

一、保障策略

保障 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 資訊被盜用的風險。

對敏感資訊,如 session ID,進行加密後再儲存到 Cookie 中,即使 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 的工作方式和安全性方面起著重要的作用。下面是它們各自的定義和作用:

  1. SameSite:

SameSite 是一個相對較新的 Cookie 屬性,用於防止跨站點請求偽造(CSRF)。它有三個可能的值:StrictLaxNone

  • Strict:Cookie 只有在同一個站點的請求中才會被髮送。
  • Lax:在導航到目標站點的 GET 請求中,或者在同一站點的請求中,Cookie 會被髮送。
  • None:Cookie 在所有請求中都會被髮送,無論是跨站點還是同站點。
  1. 域(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 的生命週期的一些基本資訊。

對於同源請求,瀏覽器會預設帶上 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);

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請求,瀏覽器會在請求中自動包含憑據,但伺服器必須指示瀏覽器返回響應,否則響應將被忽略。

瀏覽器層

每個瀏覽器對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)。

三、總結與建議

  1. 預設情況下,Cookie 的生命週期為臨時會話級,而在最新的瀏覽器中,SameSite 的預設值設為 Lax,已具備較高安全性。根據實際需求或請求協議,可將 Cookie 設定為 HttpOnly 或 Secure,以進一步提升安全級別。
  2. 除非必要,否則不建議設定過期時間和作用域,以免無意間擴大 Cookie 的生命週期和作用範圍。
  3. 除非必要,否則不建議在 XHR 和 Fetch 請求中擴大 Cookie 的處理策略。

九、引文

相關文章