JWT を decode して header に:
{ "alg": "none" }
が見えたら、私は通常の signed token として扱うのを止めます。alg: none は 署名なし JWS を意味し、header と payload を保護する signature / MAC がありません。
この形式がすべての文脈で禁止されるわけではありません。しかし、token 自身に「私は署名なしで受け入れてよい」とセキュリティ方針を決めさせることはありません。
最初に確認するのは:
この token type / protocol で署名なし JWT を許可することを application が明示的に決めているか?
です。答えが no なら拒否します。
alg: none が実際に意味すること
RFC 7518 は none を署名なし JWS の algorithm と定義しています。compact representation は:
header.payload.
のように 3 セグメントですが、最後の signature は空です。
JWT デコーダー で header / payload を読むことはできます。しかし、そこに署名検証の対象はありません。
token header をセキュリティ方針にしない
危険な考え方は:
if (header.alg === "none") {
acceptWithoutSignature();
}
です。
alg は未検証 token の header にある値です。authentication 前の入力に「署名検証が必要か」を決めさせてはいけません。
私は application config に:
const allowedAlgorithms = ["RS256"];
のような allowlist を持たせ、受け取った alg がその集合に含まれるかを確認します。
RFC 8725 も application が許可する algorithms を明示し、それ以外を自動で cryptographic processing に使わないよう求めています。
過去の脆弱性をどう理解するか
典型的な alg: none 問題は「JWT standard が署名なし token を常に信頼せよ」と要求したことではありません。
問題は一部 implementation が token-provided algorithm を緩く扱い、none を自動的に有効な verification mode のように受け入れたことです。
signed token の header を none に書き換え、signature を消した token を壊れた verifier が accept する可能性がありました。
そのため私は decoder に「悪意ある token 判定」をさせるより、verifier の明示的な allowlist を重視します。
decode と verify を分ける
alg: none の payload も普通に読めます。
{
"sub": "user_123",
"role": "admin",
"exp": 1789203600
}
しかし signature / MAC がないなら、誰でもこれらの claim value を作れます。
私は次の流れを使いません。
payload を読めた
→ claims が自然に見える
→ authenticated user と判断
authentication が必要なら、application が定めた cryptographic protection と validation rules を要求します。
none を許せる特殊な文脈
RFC 7518 / RFC 8725 は署名なし JWT の存在自体を否定していません。別 mechanism で cryptographic protection が提供されるなど、integrity が別で保証される文脈では成立します。
ただし重要なのは 明示的に要求されていることです。
私は application 全体に:
allowUnsignedJwt = true
のような広すぎる switch を作りません。必要なら対象 token type を隔離し、通常の signed JWT validation とは別の policy にします。
「すべての algorithm を support」しない
IdP が RS256 を使うなら、例えば:
Allowed: RS256
Rejected: HS256, none, ES256, others
とします。deployment が追加 algorithm を意図的に support する場合だけ allowlist を増やします。
JWT 署名検証 と JWKS Validator も unknown alg を実行命令として扱わず、対応する既知の algorithm / key compatibility を確認します。
invalid signature と alg none は別の状態
Invalid signature は:
signature は存在するが verification が失敗した
alg none は:
signature algorithm がなく、signature segment も空
です。
production token が突然 RS256 から none に変わったなら、public key を変えて試すのではなく token source と verifier policy を調べます。
alg だけで acceptance を決めない
強い allowed algorithm を使っていても、次の checks は必要です。
- signature verification
- trusted issuer
- audience
exp/nbf- token type
- scope / role
alg none は分かりやすい extreme case ですが、JWT validation は algorithm だけでは完了しません。
server verifier で欲しい設定
library API は違っても概念は:
verifyJwt(token, trustedKey, {
algorithms: ["RS256"],
issuer: "https://login.example.com/",
audience: "api://orders"
});
です。allowed algorithm は token ではなく、信頼する application config から来ます。
production で alg: none を見たときの checklist
- header が本当に
"alg":"none"か確認する。 - 署名なし JWS として signature segment が空か確認する。
- どの service が token を生成したか特定する。
- 信頼する config から期待する algorithm を確認する。
- verifier に algorithm allowlist があるか確認する。
- その token type で
noneが明示許可されていなければ reject する。 - token-type confusion や fallback verification path がないか確認する。
- logs で同様の unexpected algorithm change を探す。
私は header を none から RS256 に編集して直そうとはしません。header を変えても valid signature は復元されません。
私のルールは 署名なし JWT を許可するか決めるのは application であり、token 自身ではない という一点です。