【実務・中級編】 JWTのアルゴリズム変更攻撃(none/HS256への強制) – オフェンシブセキュリティ & リバースエンジニアリング防御ガイド

JWTの「署名検証」を骨抜きにする:アルゴリズム変更攻撃の深層と鉄壁の防御策

現場でコードをレビューしていると、JWT(JSON Web Token)を「単なるセッションIDの代わり」として軽く扱っているケースに遭遇する。だが、JWTは単なる文字列ではない。設計を誤れば、攻撃者にとっての「黄金のチケット」に早変わりする代物だ。

今日は、数ある攻撃手法の中でも、認証の根幹を崩壊させる「JWTアルゴリズム変更攻撃」について、現場の知見を詰め込んで解説する。

—

1. なぜ「アルゴリズム変更攻撃」が成立するのか

JWTは、Header.Payload.Signature の3つのパートで構成される。このうち、Header には alg (アルゴリズム)が含まれている。

問題は、多くのライブラリが「どの署名検証アルゴリズムを使うか」を、トークンに含まれるこの alg ヘッダーを信頼して判断してしまう点にある。攻撃者はここを逆手に取る。

シナリオA:none アルゴリズムへの強行

サーバー側が「署名なしでもOK」という実装を許容している場合、攻撃者は alg: none に書き換えたヘッダーを送りつける。サーバーは「署名検証は不要だな」と誤認し、悪意ある Payload をそのまま受け入れてしまう。

シナリオB:HS256(対称鍵)への強制

公開鍵暗号(RS256)で署名されているトークンに対し、攻撃者はわざとアルゴリズムを HS256 に書き換える。サーバー側が「公開鍵」を「秘密鍵(共有鍵)」として解釈し、公開鍵そのものを署名の検証キーとして使ってしまうという、非常に有名な実装ミスだ。公開鍵は誰でも手に入るため、攻撃者は自分の秘密鍵で署名を偽造できる。

—

2. 実践的な防御策:コードで語る鉄壁の実装

ライブラリをデフォルトで使うのが一番危ない。「期待するアルゴリズム以外は徹底的に弾く」というホワイトリスト方式が唯一の正解だ。

Python (PyJWT) でのセキュアな実装例

PyJWT を使用する場合、必ず algorithms 引数を明示すること。これにより、ライブラリの脆弱性を突かれても、指定外のアルゴリズムを強制的に拒否できる。

import jwt

# サーバー側が想定している公開鍵(実際の運用環境からロードすること)
PUBLIC_KEY = "-----BEGIN PUBLIC KEY-----\n..." 

def verify_token(token):
    try:
        # 【重要】algorithmsをリストで明示する。これ以外のアルゴリズムはエラーになる。
        # 'none' や 'HS256' を含めないことが鉄則。
        decoded_payload = jwt.decode(
            token, 
            PUBLIC_KEY, 
            algorithms=["RS256"] 
        )
        return decoded_payload
    except jwt.InvalidAlgorithmError:
        # ここにログを仕込んでおくと、攻撃の予兆を検知できる
        print("警告:不正なアルゴリズムによるアクセス試行")
        return None
    except Exception as e:
        return None

JavaScript (Node.js/jsonwebtoken) での実装例

Node.js環境でも同様だ。verify 関数のオプションを厳格に設定する。

const jwt = require('jsonwebtoken');

const verifyOptions = {
    // 【重要】許容するアルゴリズムを厳密に制限
    algorithms: ['RS256'],
    issuer: 'my-auth-server' // 発行者の検証もセットで行うのが定石
};

function verifyToken(token, publicKey) {
    try {
        return jwt.verify(token, publicKey, verifyOptions);
    } catch (err) {
        // 検証失敗時はログを残してセッションを破棄する
        console.error('JWT検証失敗:', err.message);
        return null;
    }
}

—

3. インフラ・アーキテクチャレベルでの防壁

アプリケーションコードでの対策は必須だが、多層防御としてゲートウェイでの制御も有効だ。

Nginx でのヘッダーフィルタリング

もしJWTをヘッダーでやり取りしているなら、alg ヘッダーが特定の不審な値でないか、Nginx側で簡易的にチェックするのも手だ(※完全な検証はアプリで行うこと)。

# Nginxの設定例: 特定の怪しいヘッダーパターンをブロック
if ($http_authorization ~* "alg%22%3A%22none") {
    return 403;
}

クラウドIAM・WAFの活用

WAF(AWS WAF等)を使用している場合、JWTの構造を解析するルールセットを適用することも可能だ。特に alg: none が含まれるリクエストをブロックするルールは、攻撃の入り口を塞ぐ高い効果がある。

—

4. 最後に:エンジニアへのアドバイス

JWTは便利だが、その柔軟性が脆弱性に直結している。

1. ライブラリを最新に保つ: PyJWT や jsonwebtoken は、過去に何度も「アルゴリズムの検証不備」でCVEが出ている。依存関係は定期的に更新しよう。
2. ログを信頼せよ: InvalidAlgorithmError や InvalidSignatureError が発生した際は、単にエラーを返すだけでなく、「誰が・いつ・どのIPから」攻撃しようとしたのかをログに記録し、SIEMや監視ツールでアラートを鳴らせ。
3. 「デフォルト」を疑え: セキュリティ設定において「デフォルトで動く」ことは「攻撃に対して無防備」と同義だ。

コードは嘘をつかない。君が書いたその一行が、明日誰かの個人情報を守るかもしれないし、逆にサーバーの鍵を奪うきっかけになるかもしれない。常に「最悪の攻撃者」の視点を忘れないでほしい。

健闘を祈る。

コメント

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