「登入後由伺服器交給你的"帶簽章的通行證"」——這就是 JWT。本文講解它的原理以及安全使用的關鍵要點(不會寫出攻擊手法)。
原理(三個部分)
JWT 是用 . 把 標頭.酬載.簽章 三部分連接而成的字串。標頭和酬載只是用 base64url 編碼(並非加密),只有簽章是用金鑰(或共用金鑰)生成的。
① 標頭
簽章演算法等元資訊。任何人都能讀
② 酬載
使用者 ID、有效期等主張。任何人都能讀=不要放機密
③ 簽章
用金鑰生成。保證未被竄改
內容「只是可讀」這一點至關重要。所以酬載不是放密碼或個人資訊的地方。JWT 的價值在於:伺服器透過校驗簽章,就能確認「這個權杖是我簽發的、且未被竄改」。
安全使用的防禦措施
必須校驗簽章,並固定 alg(最重要)
在伺服器端每次都校驗簽章。把允許接受的簽章演算法(alg)固定為期望的值,並拒絕 alg:none(無簽章)。不要輕信權杖標頭裡寫的 alg。
不要把機密放進酬載
要以"內容人人都能解碼"為前提。密碼、個人資訊、API 金鑰等機密不要放進去。只放識別碼或權限等即便外洩也不致命的主張。
把有效期設短,並準備失效手段
存取用權杖要設為短命。過長的有效期會延長權杖被盜後的受害時間。如果設計上需要失效,就把短命的存取權杖+伺服器端管理的刷新/工作階段搭配使用。
使用強金鑰,並存放在不會被盜的地方
簽章金鑰要足夠強,不要重複使用,並保管在伺服器端的安全位置。權杖本身也要連同保管與傳輸一起守護,比如不被 XSS 竊取、用帶安全屬性的 Cookie 傳輸(→ 注意經由 XSS 的竊取)。
本站觀點:JWT 並非「萬能的工作階段」
JWT 常被用於「保存登入狀態」,但它有一個弱點——不擅長失效。只要簽章正確且在有效期內,伺服器基本上就會信任它,因此很難讓登出或失效立即生效。本站的立場是:「不要把 JWT 當作長命的萬能工作階段」。把短命的存取權杖,與伺服器端可撤銷的刷新/工作階段組合起來,就能兼顧 JWT 的優點(校驗輕量)和失效的便利性。選對用途才是正解。
盲點:「能解碼」不等於「正確」
JWT 的內容是可讀的,所以只要貼進 JWT 解碼器,任何人都能查看標頭和酬載。但是解碼(讀取內容)與校驗(確認真偽)完全是兩回事。能解碼並不能保證任何正確性。判定權杖真偽的,永遠是伺服器端的簽章校驗。除錯時查看內容很方便,但請不要誤以為「能讀出來就有效」。
接下來閱讀
- 工具:JWT 解碼器 / 檢查(查看內容、檢查 alg:none 或是否過期)
- 術語:XSS 是什麼(竊取權杖的典型路徑)/ CSRF 是什麼
- 入門:如何選擇雙因素認證(MFA)
FAQ
QJWT 的內容是加密的嗎?
不是。標準 JWT 的標頭和酬載並非加密,只是用 base64url 做了「編碼」,任何持有權杖的人都能讀到裡面的內容。所以絕不能把密碼、個人資訊等機密放進酬載。JWT 保護的不是「內容的機密性」,而是「未被竄改(靠簽章保證的完整性)」。
QJWT 中最危險的設定是什麼?
不校驗簽章,或者接受「alg:none(無簽章)」的實作。一旦允許,攻擊者就能讓自己隨意改寫內容的偽造權杖通過。防禦方法是:在伺服器端必須校驗簽章,並把允許接受的簽章演算法(alg)固定為期望的值。
Q解碼和校驗一樣嗎?
不一樣。解碼只是讀取內容,任何人都能做(用 JWT 解碼器即可查看)。校驗則是伺服器用金鑰/公開金鑰確認簽章是否正確、是否在有效期內、簽發者和受眾是否正確的處理過程。「能解碼=就是正確的權杖」是錯誤的。判定真偽的永遠是伺服器端的校驗。