JWT 指南

我如何理解並防守 JWT alg=none

alg=none 代表沒有密碼學簽章的 unsecured JWS。我會用固定 algorithm allowlist、trusted issuer/key source 與 negative tests,避免 token 自己決定 verifier policy。

作者:JWTDecoder.tools最後確認 2026-09-13

編輯語氣說明:第一人稱敘述代表 JWTDecoder.tools 已文件化的驗證工作流程;發布者與組織作者皆為 JWTDecoder.tools。

alg=none ∉ application allowlist
token alg → application allowlist → accept / reject

看到 JWT header 裡的 "alg": "none" 時,我不把它當成另一種正常簽章演算法,而是理解成 沒有密碼學簽章的 unsecured JWS。如果目前 endpoint 預期的是 signed access token,none 通常就應該被拒絕。

真正的防守重點不是只寫一個 if (alg === "none"),而是讓 verifier policy 先決定允許哪些 algorithm,而不是讓未驗證的 token 自己決定。

alg=none 表示什麼

一般 signed JWT:

header.payload.signature

unsecured form 的 header 可能是:

{
  "alg": "none",
  "typ": "JWT"
}

signature segment 可能是空的。這種 token 的 payload 可以正常 decode,但沒有 signature 證明內容由可信任 signer 產生。

危險的 verifier 流程

我避免這種邏輯:

先讀 token header
→ token 說 alg=none
→ verifier 因此跳過 signature
→ 直接相信 payload

攻擊者可以自行建立 header/payload,所以未驗證 token 不能決定 security policy。

我使用的方向是:

application config 先設定 allowed algorithms
→ token alg 必須在 allowlist
→ 否則 fail closed
→ 再用對應 trusted key 驗 signature

例如 API 只接受 RS256:

allowed algorithms = ["RS256"]

那 none、HS256 或其他 unexpected value 都不應改變 verifier 的規則。

所有 alg=none 都是攻擊嗎

不一定。某些明確協議情境可能定義 unsecured JWT 的合法用途。但我不會把「標準存在這個值」理解成「一般 API authentication 應該接受」。

我會先回答:

  • 目前 endpoint 是否明確要求 signed token?
  • protocol profile 是否明確允許 unsecured JWT?
  • token 是否會影響 authentication/authorization?
  • 是否存在另外一層被規範的完整性保護?

沒有明確需求時,我不會開啟 none。

Decode 成功不代表 signature valid

這是 alg=none 最容易讓人混淆的地方。

Decoded
→ header/payload 可讀

Verified
→ trusted key 驗證簽章

Accepted
→ issuer/audience/time/type policy 通過

none token 完全可能第一層成功,但在要求 signed JWT 的系統裡不能進入第二層。

Empty signature 也要看 algorithm

如果 header 是:

{ "alg": "RS256" }

但 signature 是空的,我會把它當 invalid signed JWT,而不是自動降級成 none。

也就是:

alg=none + empty signature
→ unsecured JWT form

alg=RS256 + empty signature
→ invalid signed JWT

我如何測試 verifier

我會保留 negative test:

valid RS256 → pass
wrong signature → fail
alg=none → fail
HS256 when only RS256 allowed → fail
empty RS256 signature → fail
wrong issuer → fail
wrong audience → fail

這些測試比只驗 happy path 更能證明 security configuration 沒有退化。

alg=none 與 algorithm confusion 的關係

兩者不是完全相同的問題,但共同根源是 verifier 過度相信 token header。不同 algorithm family 有不同 key semantics,不能讓 token 任意切換 verifier 行為。

RFC 8725 的實務方向也是使用明確 algorithm allowlist,並把 token type、issuer、audience 等政策分開驗證。

Key source 一樣不能由 token 自己決定

即使已經禁止 none,header 仍可能包含 kid、jku、x5u。

我不會自動追 token 提供的 remote key URL。我會從 configured trusted issuer 的 metadata/JWKS 出發,在 trusted key set 裡用 kid 選 key。

JWKS Validator 可以重現這個流程。

我會用 Security Analyzer 看哪些訊號

JWT Security Analyzer 會把:

  • alg=none;
  • missing algorithm;
  • unexpected algorithm;
  • empty signature;
  • token-controlled key URL;
  • 可疑 kid;

分開呈現。

我把這些當成 policy signal,不會把「沒有發現 signal」包裝成「token 已經可信」。

不同 token type 要有不同 validation rules

ID token、access token、password reset token、email verification token 可能都使用 JWT。若全部共用一套過度寬鬆的 verifier,就可能發生某類 token 被錯用到另一個 context。

我會依用途明確設定:

  • expected issuer;
  • expected audience;
  • allowed algorithm;
  • token type/profile;
  • required claims。

我不採用的修法

  • 只要 payload 看起來合理就接受;
  • 為了相容舊 client 開啟所有 algorithms;
  • 開發環境允許 none,但沒有防止設定進 production;
  • signature fail 時自動 fallback 到 decode-only;
  • token 的 jku 說去哪裡就去哪裡拿 key。

我的 alg=none 防守清單

  1. 每個 verifier 明確定義 allowed algorithms。
  2. token header 不能修改這份 policy。
  3. 一般 signed-token endpoint 明確拒絕 none。
  4. signed algorithm 配 empty signature 要 fail。
  5. algorithm 與 key type 必須相容。
  6. key source 綁定 trusted issuer。
  7. signature 通過後仍驗 iss、aud、time、token type。
  8. 保留 none 與 unexpected algorithm 的 regression tests。

我看到 alg=none 時,真正要確認的不是「這個字串危不危險」,而是 verifier 是否始終由自己的 trust policy 控制,而不是被未驗證 token 控制。

來源與參考資料

相關指南