JWT(JSON Web Token)是用點號連接的三段 Base64url 編碼內容。解碼一份 JWT 來讀取 claims 只需要一次函式呼叫,且不需要任何金鑰 —— 這恰恰說明了 JWT payload 不是放敏感資料的安全地方。本指南講解 JWT 是什麼、如何解碼每一部分,以及解碼與驗證之間至關重要的差別。
什麼是 JWT?
JWT 是一種緊湊、URL 安全的 token,用來在各方之間攜帶 claims —— 通常是使用者身分與權限。它由 RFC 7519 標準化。一個 token 看起來像這樣:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0IiwibmFtZSI6IkFkYSJ9.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk兩個點號把它切分為三部分:header、payload 與 signature。
三個組成部分
- Header —— 描述簽章演算法的一個小 JSON 物件,例如
{"alg":"HS256","typ":"JWT"}。 - Payload —— 一組 claims 的 JSON 物件,例如
sub(subject)、exp(過期時間),以及任何自訂欄位。 - Signature —— 對 header 與 payload 的密碼學簽章,用於偵測篡改。 它不是人類可讀的,也不是 JSON。
header 與 payload 都是 Base64url 編碼的 JSON。簽章由 JWS(RFC 7515)產生。
如何在 JavaScript 中解碼 JWT
依點號切分,然後對前兩段做 Base64url 解碼。注意 JWT 使用的是Base64url(把 + 與 / 換成 - 與 _,並且不填補),因此解碼前需要做一次換字元:
function decodeJwt(token) {
const [headerB64, payloadB64] = token.split('.');
const decode = (b64url) => {
// Base64url → Base64,再解碼並 parse
const b64 = b64url.replace(/-/g, '+').replace(/_/g, '/');
const json = atob(b64);
return JSON.parse(json);
};
return { header: decode(headerB64), payload: decode(payloadB64) };
}
decodeJwt(token);
// {
// header: { alg: "HS256", typ: "JWT" },
// payload: { sub: "1234", name: "Ada" }
// } 為了完整的 Unicode 安全,請把 Base64 解碼為位元組,再透過 TextDecoder 處理,而不是只用 atob。
如何在 Python 中解碼 JWT
import base64, json
def decode_jwt(token):
header_b64, payload_b64, _sig = token.split('.')
def decode(part):
# 補回 Base64url 剝掉的填補
padded = part + '=' * (-len(part) % 4)
return json.loads(base64.urlsafe_b64decode(padded))
return decode(header_b64), decode(payload_b64)解碼不是驗證
這是關於 JWT 最重要的一點。任何人都能解碼一個 token 並讀它的 payload —— 不需要任何金鑰。解碼告訴你 token 聲稱了什麼;但它並不告訴你這些 claims 是否可信。
要信任一個 token,你必須以經過審 計的函式庫驗證簽章(對照伺服器的金鑰或公鑰)—— 不要自己造輪子:
import jwt from 'jsonwebtoken';
// 驗證簽章 AND 檢查過期;不通過就拋錯
const claims = jwt.verify(token, process.env.JWT_SECRET);
// jwt.decode(token) —— 不驗證;僅用於查看伺服端的驗證才讓 JWT 成為憑證。把解碼後的 payload 當作可信、卻不去檢查簽章 —— 這是經典的認證漏洞。
JWT payload 沒有被加密
因為 payload 只是 Base64url 編碼的 JSON,它提供了零 的機密性。這與 Base64 不是加密 談到的是同一個誤解:編碼任何人都能反向。
永遠不要把密碼、完整信用卡號或敏感個人資訊放進 JWT payload。如果你真的需要一個加密的 token,請用 JWE(JSON Web Encryption,RFC 7516)—— 這是另一個、相對少見的標準。也避免把真實 token 貼進不可信的線上解碼器;見 為什麼不要把敏感 JSON 貼到線上工具中。
驗證順序:先驗簽,再看 claims
在生產環境中驗證一個 token 時,順序很重要。一份安全的檢查清單:
- 演算法白名單 —— 不在你期望集合中的一律拒絕(例如
['RS256'])。永遠不要接受none。 - 簽章 —— 用正確的金鑰驗證(按
kid在 JWKS 裡查)。 exp—— token 不能已過期。nbf(not-before)—— token 不能太早被使用。iat—— 合理性檢查:簽發時間不能荒謬地落在未來(時鐘漂移除外)。iss—— 簽發者必須是你預期的可信權威。aud—— audience 必須包含你的服務。
多數 JWT 函式庫預設會做 1-4 步,並要求你明確啟用 6-7。在 iss 與 aud 被檢查之前,一個本來用於你生態中另一個服務的合法 token,可能被重放到你的服務上。
常見標準 claims
| Claim | 含義 |
|---|---|
iss | 簽發者 —— 建立 token 的人 |
sub | 主體 —— token 描述的是誰(通常是使用者 ID) |
aud | 受眾 —— token 的目標接收方 |
exp | 過期時間 —— 此 Unix 時間戳之後 token 無效 |
iat | 簽發時間 —— token 建立時的 Unix 時間戳 |
nbf | 不早於 —— 此時間之前 token 無效 |
kid header 與 alg: none 陷阱
在真實 JWT 中出現得太頻繁的兩個 header 欄位,值得讓你一眼就認出來:
kid(key ID) —— 一個提示,告訴驗證方:在一個金鑰集(JWKS)中哪一把 簽了這個 token。它放在 header 裡,這樣驗證方在驗簽之前就能挑對金鑰。請把kid當成不可信輸入:在固定白名單(或拉取到的 JWKS)裡查它,永遠不要把它當作檔案路徑、SQL 參數或 URL 使用。alg: "none"—— 一個合法但歷史上極危險的演算法,意思是「無簽章」。解碼器照樣能解碼,寬鬆的驗證方甚至會接受 —— 任何偽造的 payload 都會變成「合法」 token。明確拒絕alg: none,並把你的函式庫設定為只接受你期望的精確演算法(例如['RS256']),而不是相信 token 自己 header 中所聲稱的演算法。
在瀏覽器中解碼 JWT
想要快速查看 token 的 claims,把任何一個 Base64url 段貼進 fixjson 的 Base64 解碼器 —— 它在本地解碼到底層 JSON,不會向任何伺服器傳送資料。對非敏感 token 你也可以用專門的除錯器,但對包含真實使用者資料的 token,本地優先的工具才是安全選擇。
常見問題
怎麼解碼 JWT?
依點號切分 token,對前兩部分(header 與 payload)做 Base64url 解碼得到 JSON。第三部分是簽章,不是人類可讀。解碼不需要金鑰。
解碼 JWT 與驗證它一樣嗎?
不一樣。解碼只是讀 claims,誰都能做。驗證是用金鑰或公鑰校驗簽章,確認 token 真實且未被篡改。信任 token 之前請始終在伺服端驗證。
JWT 中的資料任何人都能讀嗎?
能。payload 是 Base64url 編碼的,不是加密 —— 對任何拿到 token 的人來說就是明文。永遠不要在 JWT payload 中存放秘密。見 Base64 不是加密。
JWT、JWS 與 JWE 有什麼差別?
一個簽章的 JWT 使用 JWS(RFC 7515)—— payload 可讀、能偵測篡改。JWE(RFC 7516)會真正加密 payload,提供機密性。你日常見到的普通 JWT 就是 JWS。
kid claim 是用來幹嘛的?
kid 是一個 header 欄位(不是 payload claim),指明 JWKS 中哪一把金鑰簽了 token,讓驗證方能挑對那把。在你掌控的固定金鑰集裡查它 —— 永遠不要把它當作檔案路徑或 URL 信任。
為什麼 alg: "none" 危險?
意思是「無簽章」。如果驗證方尊重 token 自 己宣告的演算法,它就會接受任何偽造的 payload。請始終把函式庫設定為只接受你期望的精確演算法(例如 ['RS256']),並明確拒絕 none。
本地檢視 token
- Base64 編碼與解碼 —— 在瀏覽器中解碼 JWT 的 header/payload 段
- Base64 不是加密 —— 為什麼 JWT payload 對任何人都可讀
- 為什麼不要把敏感 JSON 貼到線上工具中 —— 安全地處理真實 token
- RFC 7519:JSON Web Token —— 標準本身