【実務・中級編】JWTの脆弱性:アルゴリズム「none」の悪用と鍵混同攻撃の防御 – アプリケーションセキュリティ & 安全な開発防御ガイド

JWTは「魔法の杖」ではない:alg: “none” と鍵混同攻撃から身を守るための実戦的防衛術

やあ。現場で泥をかぶっているエンジニア諸君、お疲れ様。
今日はJSON Web Token(JWT)の話をしよう。「ログイン状態を保持するのに便利だから」と、何の気なしにライブラリのデフォルト設定を信じて実装していないか?

JWTは非常に洗練された仕様だが、その柔軟性が仇となり、多くのシステムが「設計ミス」という名の爆弾を抱えている。特に、攻撃者が好んで狙う「アルゴリズムの脆弱性」は、システムの門番を無効化する最も古典的かつ致命的な手法だ。今回は、理論ではなく「現場でどう防ぐか」に絞って解説する。

1. 攻撃者が狙う「alg: none」という盲点

JWTのヘッダーにある `alg` フィールドは、署名の検証アルゴリズムを指定するものだ。開発者がライブラリの検証関数を「適当に」呼び出すと、攻撃者はこのヘッダーを書き換えてくる。

攻撃シナリオ:alg: “none”

攻撃者は、本来 `HS256` 等で署名されているトークンのヘッダーを以下のように書き換える。

{
“alg”: “none”,
“typ”: “JWT”
}

この状態で、ペイロードの `user_id` を `admin` に改ざんして送信する。もしサーバー側のライブラリが `alg` のチェックを強制していなければ、ライブラリは「アルゴリズムなし=署名検証は不要」と判断し、いとも簡単に管理者権限を奪取されてしまう。

2. 鍵混同攻撃(Key Confusion Attack):より巧妙な罠

これは `RS256`(公開鍵暗号)を使用しているシステムで特に注意が必要だ。
攻撃者は、公開鍵を「HMAC用の共有鍵」として解釈させる。

1. サーバーが持つ「公開鍵(Public Key)」をBase64で取得する。
2. 攻撃者は、その公開鍵を「共有鍵」として使用し、HS256で署名を生成する。
3. サーバー側は、公開鍵を検証鍵として期待しているにも関わらず、HMAC(HS256)として扱ってしまい、署名が整合してしまう。

3. 実践:セキュアな実装ガイド

「ライブラリに任せれば安全」という考えは捨てろ。「期待するアルゴリズム以外は絶対に受け付けない」という制約をコードに刻み込むのが、プロの仕事だ。

Python (PyJWT) での防衛実装

デフォルトでアルゴリズムを指定せず `decode()` を呼ぶのは自殺行為だ。以下のように、明示的にアルゴリズムを制限する。

import jwt

期待するアルゴリズム以外を弾く!これが鉄則
def verify_token(token, public_key):
try:
# algorithms引数を指定しないと、脆弱なalgが通る可能性がある
payload = jwt.decode(
token,
public_key,
algorithms=[“RS256”] # 他のアルゴリズムは一切認めない
)
return payload
except jwt.InvalidAlgorithmError:
# ログを吐いて即座に遮断
log.error(“不正なアルゴリズムが検出されました”)
return None

Node.js (jsonwebtoken) での防衛実装

JS界隈でも同様だ。`verify` 関数にオプションを渡すことを忘れるな。

const jwt = require(‘jsonwebtoken’);

const verifyToken = (token) => {
try {
// algorithmsを配列で指定。ここが防御の最前線
return jwt.verify(token, publicKey, { algorithms: [‘RS256’] });
} catch (err) {
console.error(“JWT検証失敗:”, err.message);
throw new Error(“Unauthorized”);
}
};

4. 運用・インフラ面での「多層防御」

コードの修正に加えて、インフラ層でも保険をかけておくのがシニアの立ち回りだ。

Nginx / WAF による防御

JWTの中に `alg: none` が含まれているようなリクエストは、アプリケーションに到達する前に捨ててしまおう。WAF(AWS WAFなど)で、リクエストボディやヘッダーのJSON内に `alg` と `none` という文字列が混在しているリクエストを検知・ブロックするルールを追加するのも有効だ。

JWTの設計原則(これだけは守れ)

1. アルゴリズムの固定: アプリ全体で使うアルゴリズムを一つに絞り、それをハードコードせよ。
2. 秘密鍵の管理: 署名用の鍵は決してソースコードに含めるな。AWS Secrets ManagerやHashiCorp Vaultを使い、環境変数経由で安全に注入すること。
3. トークンの寿命: `exp`(有効期限)を短く設定せよ。万が一漏洩しても、被害を最小限に抑えるためだ。

最後に:なぜ「泥臭い確認」が必要なのか

セキュリティの現場では、ドキュメントに書いてある通りに動くことは稀だ。ライブラリのアップデートでデフォルトの挙動が変わることもある。

「動くコード」を書くのはジュニアエンジニアの仕事だ。我々がやるべきは、「どうすればこのコードが攻撃者に悪用されるか?」という疑念を常に持ち続けることである。

JWTの検証を実装したら、必ず一度はヘッダーを `none` に書き換えて、自分のサーバーに投げつけてみるんだ。「401 Unauthorized」が返ってきて初めて、君のコードは本番環境にデプロイする資格を得る。

さあ、今すぐ君のレポジトリをチェックしてくれ。健闘を祈る。

コメント

タイトルとURLをコピーしました