JWT 解碼工具

以示範資料取代目前的輸入。

預期結果: 標頭顯示 HS256,內容顯示 sub = demo-user。簽章刻意設為無效,這不是登入權杖。

限制: 解碼不代表驗證身分。選用的簽章檢查支援 HS256/384/512;應用程式仍須檢查簽發者、受眾與時間宣告。

解碼不等於驗證。任何人都能讀取 JWT 內容,也能偽造一個可正常解碼的權杖。解碼結果只顯示權杖宣稱了什麼,不代表這些內容為真。所有處理都在您的瀏覽器中完成,權杖不會傳送到任何地方。

您輸入的內容不會被儲存、記錄或分享。

貼上 JWT 即可檢視其標頭與內容,並看到到期時間。解碼完全在您的瀏覽器中進行,權杖不會離開您的裝置。

使用方式

  1. 將權杖貼到輸入框。
  2. 標頭、內容與時間宣告會顯示在下方。
  3. 如有需要,可輸入密鑰驗證 HS256 或 HS512 簽章。

解讀三個區段

JWT 是以點號連接的三個 base64url 編碼區段;這裡的 base64url 不加填充,所以區段結尾不會像一般 Base64 那樣出現等號。

第一個區段是標頭:一個小型 JSON 物件,在 alg 中註明簽章演算法,通常在 typ 中註明類型,有時還會在 kid 中放入金鑰識別碼,讓驗證方知道要用多把金鑰中的哪一把來檢查。第二個區段是酬載,也就是由宣告組成的 JSON 物件。第三個區段是簽章,它是原始位元組而不是 JSON,所以這裡以十六進位顯示。

簽章涵蓋的是前兩個區段原樣以點號連接後的內容,也就是編碼後的文字,而不是它所代表的 JSON。這就是為什麼權杖不能美化排版、重新序列化或調換宣告順序,否則就會失效;也是為什麼解碼器雖然顯示宣告內容,卻把原始文字視為真正重要的東西。

解碼不等於驗證

這是 JWT 最常被誤解的地方,所以值得毫不含糊地說清楚。酬載只是編碼,並沒有加密。任何持有權杖的人都能讀到其中每一項宣告。任何人都能建立一個酬載寫著任意內容的權杖,而它在這裡和其他任何地方都能完美解碼。

解碼後的權杖給你的是一組主張,而且沒有任何證據。證據是簽章,而檢查簽章需要金鑰。在你用事先選定的金鑰、事先選定的演算法完成檢查之前,權杖告訴你的,只是某人希望你相信的內容。

實務上的意思是:永遠不要根據解碼後的酬載做授權決策。在除錯時解碼權杖看看裡面有什麼沒有問題。但因為權杖經過了「應該已經檢查過」的閘道,就在應用程式碼中不驗證直接讀取 sub 宣告,服務就是這樣開始信任偽造身分的。本頁提供可選的 HS256 驗證,正是為了讓這個差別清楚可見。

宣告內容,以及誰負責檢查

RFC 7519 註冊了七個宣告。iss 是簽發者,sub 是權杖所描述的主體,aud 是預期的接收者,jti 是權杖本身的唯一識別碼。三個時間宣告 exp、nbf 和 iat 是 NumericDate 值,單位是自 Unix 紀元起算的秒數,而不是毫秒;這是手動建立的權杖遭拒最常見的原因。

這些宣告沒有一個會自己生效。exp 已經過去的權杖之所以算過期,只是因為驗證方拿它與時鐘比較後拒絕。為其他接收者簽發的權杖之所以危險,只在沒人檢查 aud 的時候;為某個服務簽發的權杖,就是這樣被拿去對共用同一把簽章金鑰的另一個服務重放。多數函式庫預設會檢查 exp,但只有在你告訴它預期值時才會檢查 aud。

簽發與驗證的機器之間時鐘有些微差異是正常的,所以驗證方通常會容許一到兩分鐘的誤差。本頁會把每個時間宣告顯示成易讀的日期,並告訴你權杖目前是否在有效期間內。

什麼時候不該用 JWT

JWT 是自帶宣告的持有者權杖,這讓它非常適合無狀態的服務間呼叫,但用來做工作階段機制就很彆扭。

彆扭之處在於撤銷。因為驗證方只需要金鑰,過程中從不查詢任何儲存區,所以沒有辦法在 exp 之前讓權杖失效。把使用者登出、變更密碼、停用帳號,已經簽發的權杖都會繼續有效,直到過期為止。常見的解法是極短的有效期搭配更新權杖,或是每次請求都檢查 jti 的黑名單,但兩者都重新引入了無狀態原本想去除的狀態。

存放位置也很重要。放在 localStorage 的權杖,頁面上執行的任何腳本都讀得到,只要有一個跨站腳本漏洞就會被竊取;httpOnly Cookie 則讀不到,代價是需要 CSRF 防護。而且權杖每次請求都放在標頭中傳送,大型酬載是有實際成本的:塞進太多宣告,就會碰上代理伺服器的標頭大小限制。

如果只是一般的網頁工作階段,搭配伺服器端儲存的隨機不透明識別碼更簡單、能立即撤銷,體積也更小。

驗證該按什麼順序做

正確的驗證是一連串步驟,漏掉任何一步就是已知攻擊的溫床。第一,事先決定要用哪個演算法,並拒絕任何 header 與之不符的權杖——絕對不要從權杖裡讀 alg 然後就相信它。第二,選出金鑰:若是共用密鑰,那就是一個固定值;若是非對稱的權杖,就是對應 header 中 kid 的公開金鑰,從發行者的 JWKS 端點取得並快取起來。

第三,就用前兩段原本的樣子重新計算簽章,並以固定時間比對。第四,而且必須等到簽章確認無誤之後,才去讀取 claim:用一個小小的容許誤差把 exp 和 nbf 與時鐘比對,確認 iss 是你預期的發行者,並確認 aud 包含你自己。

只要設定妥當,多數函式庫都會完成以上所有步驟,而大多數漏洞都來自應用程式自己動手做了其中一部分。在驗證前先解碼來讀某個 claim,在除錯器裡沒問題,在請求處理流程裡卻很危險,因為在那個當下,權杖仍然只是某個人的片面說法。

JWT 在 OAuth 與 OpenID Connect 裡的位置

在 OpenID Connect 裡,id_token 是一個 JWT,而且是給用戶端看的:它說明誰登入了、什麼時候登入、以及怎麼登入的。存取權杖則是另一回事,是給 API 用的;雖然許多供應商把它發成 JWT,規範並沒有這樣要求——它也可以是一串不透明的字串,由 API 透過 introspection 來驗證。

這個區別很重要,因為它決定了你的 API 該怎麼做。如果存取權杖是不透明的,API 就要呼叫供應商的 introspection 端點;如果它是 JWT,API 就用公開的金鑰在本地驗證。把 id_token 當成存取權杖使用是很常見的整合錯誤:它是發給另一個 audience 的,而一個接受它的 API,等於會接受同一個供應商為任何應用程式所簽發的權杖。

權杖裡的 scope 和角色是發行者的斷言,而不是權限本身。呼叫者能不能做某件事仍然由 API 決定;權杖只說明呼叫者是誰,以及發行者願意為什麼背書。

一再重演的漏洞

alg: none 這個把戲宣稱權杖沒有簽章,十年前有好幾個函式庫真的會接受。演算法混淆則更隱晦:拿一個 RS256 的權杖,把 header 改成 HS256,再用發行者的公開金鑰當作 HMAC 的密鑰去簽名——會從權杖裡讀取演算法的驗證端就會接受它,因為公開金鑰本來就不是秘密。

在真實世界裡最常見的發現是共用密鑰太弱。一個 JWT 本身就包含了離線測試候選密鑰所需的一切,攻擊者的硬體能跑多快就試多快,所以一個字典裡有的單字幾秒就會被破解,之後任何人都能簽發帶有任意 claim 的權杖。

再來就是各種邏輯錯誤:漏掉 aud 檢查,導致某個服務的權杖可以拿去通過另一個共用同一把金鑰的服務;用 kid 參數從某個路徑載入金鑰卻沒有清理輸入;以及接受來自查詢字串的權杖,結果它出現在伺服器日誌、瀏覽器歷史紀錄和 referrer 標頭裡。

網頁應用該用 session 還是權杖

對一個有自家後端的單一網頁應用來說,使用不透明 cookie 識別碼的伺服器端 session 幾乎在每個面向都更簡單也更好:撤銷立即生效、cookie 很小、內容永遠不離開伺服器,也沒有簽章可以搞砸。代價是需要一個 session 儲存區,而對多數應用而言那不過是一張資料表或一個 Redis 鍵。

JWT 真正站得住腳的場合,是驗證方無法去問發行者的時候:跨越信任邊界的服務對服務呼叫、提供給第三方使用的 API,或是必須不經來回往返就驗證請求的無狀態邊緣節點。這些都是真實存在的限制,遇上了就值得承擔額外的複雜度。

多數團隊最後採用的折衷是:短命的存取權杖(以分鐘計,不是以小時計),搭配壽命較長的更新權杖,放在 httpOnly cookie 裡並同時存在伺服器端,以便隨時撤銷。這樣既讓常見路徑享有無狀態驗證,又保留了一個可以說不的地方。

瀏覽器裡該把權杖放哪裡

localStorage 可以被頁面上執行的任何一段指令碼讀取,所以只要同一個來源的任何地方有一個跨站指令碼漏洞——包括第三方的分析或廣告指令碼——權杖就會外流。很多教學都預設這麼做,但它是所有可選方案中最脆弱的一個。

帶有 httpOnly、Secure、SameSite 的 cookie 完全無法被指令碼讀取,這消除了那一整類攻擊,但也帶來了 CSRF 防護的需求,因為瀏覽器會自動附上 cookie。SameSite=Lax 能擋掉大部分情況,剩下的用一個防偽造權杖補上。

只把權杖放在記憶體裡——存成應用程式裡的一個變數,並在頁面載入時透過以 cookie 驗證的端點重新取得——是常見作法中最安全、也最不方便的一種,因為每次重新載入都要多一次更新的往返。無論選哪一種,權杖每次請求都還是會放在標頭裡傳送,所以讓內容保持精簡不只是隱私考量,也是效能考量。

常見問題

能夠解碼是否代表這個 JWT 有效?
不是,這是理解 JWT 最重要的一點。內容只是 base64url 編碼,並未加密或受保護:任何人都能讀取,也能製作出可完美解碼的偽造權杖。唯有以正確的金鑰驗證簽章,才能證明權杖為真且未被竄改。
在這裡貼上真實權杖安全嗎?
解碼與驗證都在本頁的 JavaScript 中執行,不會上傳、記錄或傳送。不過請把 JWT 視為有效的憑證:若它可能已在其他地方外洩,應直接撤銷,而不是只考慮貼到哪裡。
為什麼只能驗證 HS256 與 HS512?
它們屬於對稱式演算法,簽章與驗證使用同一組密鑰,因此可用您提供的密鑰在本機完成驗證。RS、ES、PS 屬於非對稱式,需要發行者的公開金鑰(通常來自 JWKS 端點),是另一件事。
exp、iat、nbf 是什麼意思?
它們是註冊宣告,值為 NumericDate,單位是自 Unix 紀元起的「秒」而非毫秒。exp 是失效時間,iat 是簽發時間,nbf 是最早可使用的時間。本工具會將它們顯示為易讀的日期。
alg: none 代表什麼?
表示這是未簽章的權杖。若函式庫接受它,就可能被偽造的權杖騙過,因此這是已知的攻擊手法,驗證方應一律拒絕。本工具也不會將這類權杖標示為已驗證。

最後更新:

參考資料

RFC 7519 — JSON Web Token

最近更新

  • — 新增可執行的範例、預期結果、限制說明及參考連結。

回報問題

請提供操作步驟、預期結果及實際結果。請移除機密與個人資料。

相關工具