深入淺出 JWT:現代 Web 驗證的原理、實踐與安全進階
JSON Web Token (JWT) 已成為現代 Web 應用程式驗證的主流標準。本文將從結構原理出發,深度解析安全性考量,並比較 Session、PASETO 等機制。同時,我們將探討 Token 撤銷策略與前端存儲的安全性權衡,幫助開發者建構更穩健的認證系統。
在現代的 Web 應用程式開發中,身份驗證 (Authentication) 與 授權 (Authorization) 是最基礎也最重要的環節。隨著微服務 (Microservices) 與單頁應用 (SPA) 的興起,傳統的 Session-based 驗證機制逐漸顯得力不從心,而 JSON Web Token (JWT) 則因其「無狀態 (Stateless)」與「輕量化」的特性,成為了目前最主流的解決方案。
本文將深入探討 JWT 的運作原理,並將其與其他常見的驗證機制進行比較,同時涵蓋進階的安全實作建議,協助開發者建構更安全、可擴展的系統。
1. 什麼是 JWT?為什麼需要它?
JWT (JSON Web Token) 是一種開放標準 (RFC 7519),它定義了一種緊湊且自包含 (Self-contained) 的內容格式,用於在各方之間以 JSON 物件安全地傳輸資訊。這些資訊可以被驗證和信任,因為它們經過了數位簽章。
傳統 Session vs. JWT
在了解 JWT 之前,我們先回顧一下傳統的 Session-based Authentication:
- 使用者登入:瀏覽器傳送帳密給伺服器。
- 伺服器建立 Session:驗證成功後,伺服器在記憶體或資料庫中建立一個 Session ID,並記錄使用者資訊。
- 回傳 Cookie:伺服器將 Session ID 透過
Set-Cookie回傳給瀏覽器。 - 後續請求:瀏覽器每次請求都會自動帶上 Cookie,伺服器拿著 Cookie 中的 Session ID 去資料庫查找使用者。
痛點:
- 伺服器負擔:伺服器需要儲存所有在線使用者的 Session 資料(Stateful)。
- 擴展性差 (Scalability):在多伺服器負載平衡 (Load Balancing) 架構下,Session 同步是一個難題(通常需要 Redis 等快取伺服器支援)。
- CORS 問題:Cookie 在跨網域 (Cross-Domain) 場景下,搭配同源策略 (Same-origin policy) 處理較為複雜。
JWT 的優勢:
- 無狀態 (Stateless):Token 本身包含了所有必要的資訊(如 User ID、過期時間),伺服器無需查詢資料庫即可驗證使用者身份。
- 跨網域友善:JWT 通常透過 HTTP Header (
Authorization: Bearer <token>) 傳輸,不受 Cookie 的同源限制。 - 適合微服務:一個微服務簽發的 Token,可以被其他信任該簽章的任務或服務直接驗證。
流程比較圖解
Session-based Flow
sequenceDiagram
participant User
participant Server
participant DB
User->>Server: 登入 (帳號/密碼)
Server->>DB: 驗證帳密
DB-->>Server: 驗證通過
Server->>Server: 建立 Session ID (存入記憶體)
Server-->>User: 回傳 Set-Cookie: SessionID
Note over User, Server: 後續請求
User->>Server: GET /api/data (帶 Cookie)
Server->>Server: 讀取 Session ID
Server->>DB: 查詢 Session 是否有效
DB-->>Server: 有效 (User A)
Server-->>User: 回傳資料
JWT Flow
sequenceDiagram
participant User
participant Server
User->>Server: 登入 (帳號/密碼)
Server->>Server: 驗證帳密 & 簽發 JWT
Server-->>User: 回傳 Access Token
Note over User, Server: 後續請求
User->>Server: GET /api/data (Header: Bearer Token)
Server->>Server: **驗證簽章 & 解碼 (無需查 DB)**
Server-->>User: 回傳資料
2. JWT 的結構原理
一個 JWT 字串由三個部分組成,中間用 . 分隔:Header.Payload.Signature。
A. Header (標頭)
通常包含兩部分資訊:Token 的類型 (typ) 和使用的簽章演算法 (alg)。
{
"alg": "HS256",
"typ": "JWT"
}
這段 JSON 會經由 Base64Url 編碼後形成第一部分。
B. Payload (內容)
這是存放實際資訊 (Claims) 的地方。Claims 分為三種:
- Registered Claims (註冊聲明):標準定義的欄位,如
iss(簽發者)、exp(過期時間)、sub(主題)、iat(簽發時間)。 - Public Claims (公開聲明):建議遵循 IANA JSON Web Token Registry 以免衝突。
- Private Claims (私有聲明):雙方約定好的自定義欄位,如
role:admin。
⚠️ 重要提醒:Payload 僅僅是經過 Base64 編碼,並未加密!敏感資料(如密碼)絕對不可放入。
C. Signature (簽章)
這是最關鍵的安全防線。公式如下:
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
your-256-bit-secret
)
伺服器持有的 Secret Key 保證了資料的完整性。若 Payload 被竄改,簽章校驗將會失敗。
3. 進階安全實作:撤銷 (Revocation) 與 滾動機制
由於 JWT 是無狀態的,一旦簽發後除非過期,否則伺服器很難主動將其作廢。以下是幾種常見的進階方案:
Token Revocation(黑名單 vs 白名單)
- 黑名單 (Blacklist):當使用者登出時,將該 Token 存入 Redis 並設定過期時間。伺服器驗證 JWT 後,需額外檢查其是否存在於黑名單。
- 優點:維持了大部分的效能,只需紀錄異常或提早失效的 Token。
- 缺點:伺服器變回「部分有狀態 (Stateful)」。
- 白名單 (Whitelist):伺服器只允許存在於資料庫中的 Token 通過。
- 優點:最高的控制力。
- 缺點:每次請求都要查庫,失去了 JWT 原本的效能優勢。
Refresh Token Rotation(滾動式更新)
為了縮短 Access Token 的有效時間(例如 15 分鐘),我們引入較長效的 Refresh Token。
- 運作流程:每次使用 Refresh Token 換取新的 Access Token 時,伺服器同時簽發一個「新的」Refresh Token 並作廢舊的。
- 防禦重放攻擊 (Replay Attack):如果同一個 Refresh Token 被使用了兩次以上,代表可能發生 Token 竊取。伺服器應立即將該系列下的所有 Token 全數失效,強制使用者重新登入。
4. 前端 Token 存儲安全性研究
這是一個經典的爭議:到底存 localStorage 還是 Cookie?
| 方式 | 優點 | 缺點 / 安全風險 |
|---|---|---|
| localStorage | 實作極簡,支援跨域傳輸,容量較大。 | 易受 XSS (跨站腳本) 攻擊:一段指令碼即可讀走所有 Token。 |
| Cookie | 可設定 httpOnly 防禦 JavaScript 存取。 | 易受 CSRF (跨站請求偽造) 攻擊:瀏覽器會自動帶上 Cookie。 |
最佳實踐方案: 建議存放在 httpOnly, Secure, SameSite=Strict 的 Cookie 中。為了防範 CSRF,後端應搭配 CSRF Token 或使用自定義 Header 驗證。
5. 現代替代方案:PASETO
PASETO (Platform-Agnostic Security Tokens) 被認為是 JWT 的繼承者。為什麼要考慮它?
- 防止演算法攻擊:JWT 允許在 Header 設定
alg: none,PASETO 則強制棄用有問題的加密模式。 - 安全性更強:它不提供太多靈活但危險的選擇,開發者較不容易因為設定錯誤而產生漏洞。
- 資料完整性:內建了更現代、更難破譯的簽章演算法(如 Ed25519)。
6. 結語
JWT 透過「以空間換取時間」的策略,完美契合了現代分散式系統的需求。然而,安全性不應僅僅依賴於標準本身,更在於開發者如何選擇存儲方式、設定過期時間以及實作 Token 滾動機制。
理解 JWT 的結構限制,並補齊撤銷與安全性權衡,你就能構建出既安全又高效的身份驗證系統。
🚀 實作演練:線上 JWT 解碼器
想親眼看看 JWT 內部結構長什麼樣子嗎?我特地開發了一個 JWT 解碼器工具,所有的解碼運算都在您的瀏覽器本地執行,保證隱私安全。
本文同步刊登於 Personal Digital HQ