【テクニカル・上級編】 JWTの署名アルゴリズムをnoneに設定する攻撃手法と検証回避 – オフェンシブセキュリティ & リバースエンジニアリング防御ガイド

JWTの欺瞞:alg: none が突きつける認証の深淵とアーキテクチャの敗北

セキュリティの最前線に身を置く者として、私は常に「設計者の性善説」がもたらす悲劇を目の当たりにしてきた。JSON Web Token(JWT)という、現代のWeb認証におけるデファクトスタンダード。その柔軟性は、時として致命的なセキュリティ・ホールへと直結する。

特に、かつて界隈を騒がせた「alg: none攻撃」は、単なる実装ミスではない。それは、プロトコル仕様の曖昧さと、ライブラリの利便性が生んだ構造的な脆弱性の典型例である。

1. なぜ「none」は認証を無効化できるのか

JWTの構造は単純だ。Base64Url(Header).Base64Url(Payload).Signature。この3つのパーツがドットで連結されている。攻撃者が標的とするのは、ヘッダー内のalgフィールドだ。

本来、サーバー側は署名を検証する際、以下のようなロジックを想定する。

// 脆弱なライブラリの実装を模した擬似コード
function verifyToken(token, secret) {
  const [headerB64, payloadB64, signature] = token.split('.');
  const header = JSON.parse(atob(headerB64));
  
  // 問題:ヘッダーのalgをそのまま信頼して検証アルゴリズムを選択している
  const algorithm = header.alg; 
  
  if (algorithm === 'none') {
    return true; // 署名なしでも通過させてしまう致命的ミス
  }
  
  return verifySignature(headerB64, payloadB64, signature, secret, algorithm);
}

この攻撃の核心は、「署名検証の手段を、検証対象であるはずのトークン自体に決定させている」という点にある。攻撃者はヘッダーを {"alg": "none", "typ": "JWT"} に書き換え、署名部分を空にすることで、サーバー側の検証ロジックをバイパスする。これはOSI参照モデルにおける認証層の破壊であり、メモリ内の検証フラグを直接書き換えるような攻撃に近いインパクトを持つ。

2. 現場で潜む「見えない」脆弱性の監査手法

ペネトレーションテストの現場で、この脆弱性を突くのはもはや儀式のようなものだ。しかし、現代の成熟したシステムでは、単なるnoneはWAFや単純なバリデーターに弾かれる。我々が探るのは、より深淵な「仕様の解釈の差異」だ。

例えば、以下のようなケースを監査対象とする。

  • アルゴリズムの混同攻撃(Key Confusion): 公開鍵暗号(RS256)を期待しているエンドポイントに対し、HS256(共通鍵)を送り込み、サーバーの公開鍵を「HMACの秘密鍵」として誤認させる手法。
  • ライブラリの「Default」挙動: 特定のJWTライブラリが、ヘッダーにalgがない場合や、不正な値を指定した際に、デフォルトで「検証なし」または「特定のレガシーアルゴリズム」へフォールバックする挙動。

監査時には、プロキシツール(Burp Suite等)でJWTのヘッダーを操作し、以下のペイロードを注入してレスポンスのステータスコードの変動を追跡する。

// 監査用ペイロード例: ヘッダーを改ざんし、署名を削除
{
  "alg": "none",
  "typ": "JWT"
}
// Base64Urlエンコードして送信: eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.payload.

3. 防御の鉄則:ホワイトリスト化と厳格なタイプセーフティ

この脆弱性を根絶するためには、ライブラリのアップデートだけでは不十分だ。アーキテクトは以下の「ガードレイル」を実装しなければならない。

A. アルゴリズムのホワイトリスト化

アプリケーションがサポートするアルゴリズムを、ハードコーディングで厳格に制限する。

# 堅牢な検証実装の例
import jwt

def secure_verify(token, public_key):
    # 検証時にアルゴリズムを明示的に指定する(noneを許可しない)
    return jwt.decode(
        token, 
        public_key, 
        algorithms=['RS256'] # HS256やnoneは一切排除
    )

B. 認証の「多層防御」

JWTはあくまで「トークンの整合性を証明する」ものに過ぎない。JWTの検証結果だけで権限を決定してはならない。バックエンド側で、常に「そのトークンが発行されたコンテキスト」と「現在のセッションの状態」を照合するセッションストア(Redis等)との突き合わせを行うべきだ。

4. 未来への展望:耐量子暗号とガードレイルの進化

今後、量子コンピュータの台頭により、RSAやECDSAといった現行の署名アルゴリズムは無力化される可能性がある。将来的なJWTの設計においては、耐量子暗号(PQC)アルゴリズムへの移行が必須となるだろう。

また、生成AIを用いたプロンプトインジェクションと同様に、認証トークン自体を「悪意ある入力」と見なすゼロトラストの視点が重要だ。入力されたJWTを直接パーサーに食わせる前に、構造上の異常を検知する軽量なガードレイル層を設ける設計が、次世代のセキュリティ標準となる。

結論:技術は「疑うこと」から始まる

alg: none は、単なる古い脆弱性ではない。それは「ライブラリを信頼しすぎること」「プロトコルをブラックボックスとして扱うこと」への強烈な警鐘である。

読者諸氏が設計するシステムにおいて、JWTは「認証」を担う魔法の杖ではない。それは単なる「署名付きのデータ構造」に過ぎないということを忘れないでほしい。実装の細部に神が宿るように、脆弱性もまた、開発者が最も気に留めない「仕様の隙間」に宿るのだから。

コメント

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