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

JWTの脆い要塞:`alg: none`と鍵混同が生む「認証の虚無」を解剖する

JWT(JSON Web Token)を単なる「便利なセッション管理トークン」と認識しているなら、それは大きな誤解だ。JWTは、暗号学的な制約と、それを実装するライブラリの「親切心」が複雑に絡み合った、極めて繊細なアーティファクトである。

多くの開発者が陥る罠は、JWTを「改ざん不可能な証明書」と信じ込み、検証ロジックをライブラリのデフォルト設定に丸投げしてしまうことにある。しかし、攻撃者にとってJWTは、パケット構造とアルゴリズムの不整合を突く、格好の実験場に過ぎない。

1. `alg: none`:仕様の「穴」を突く古典的かつ凶悪な手口

JWTのヘッダーにある`alg`フィールドは、検証アルゴリズムを宣言するものだ。この設計の致命的な欠陥は、仕様上「署名なし」を表す`none`が許容されている点にある。

攻撃者は、JWTのペイロードを改ざん(例:`{“user_id”: 1}` を `{“user_id”: 0}` に書き換え)した後、ヘッダーを以下のように書き換える。

// 攻撃用ヘッダーの例
{
“alg”: “none”,
“typ”: “JWT”
}

このトークンを検証する際、脆弱なライブラリは`alg`を確認する前に「このトークンは署名検証不要である」というシグナルを優先してしまう。結果、バックエンドは署名の検証をスキップし、悪意あるペイロードを正当なものとして受け入れてしまう。

防御策:アルゴリズムの静的固定(Hard-coding)

ライブラリの汎用的な検証関数(`verify()`)をそのまま使うのはやめろ。必ず期待するアルゴリズムを明示的に指定し、リスト外のアルゴリズムを即座に拒絶するガードを設けるべきだ。

import jwt

脆弱な実装(絶対に行わないこと):
decoded = jwt.decode(token, options={“verify_signature”: False})

安全な実装例:
def verify_jwt_securely(token, public_key):
try:
# アルゴリズムをRS256に固定し、それ以外を一切受け付けない
return jwt.decode(
token,
public_key,
algorithms=[“RS256”] # ここで期待するアルゴリズムのみを強制する
)
except jwt.InvalidAlgorithmError:
# 攻撃の予兆をログに記録し、管理者にアラートを飛ばす
log_security_event(“不正なアルゴリズムが検出されました”)
raise

2. 鍵混同攻撃(Key Confusion):対称鍵と公開鍵の境界を壊す

次に狙われるのが、RS256(非対称鍵)とHS256(対称鍵)の混同だ。攻撃者は、サーバーが公開鍵を使って署名を検証していることを知りながら、あえて「公開鍵をHMACの共有鍵として使う」という巧妙な攻撃を行う。

具体的には、サーバーの公開鍵をHS256の「秘密鍵」として署名を生成し、ヘッダーを`alg: HS256`に書き換える。サーバー側が「鍵の種類を検証せず、ヘッダーの`alg`に従って署名生成アルゴリズムを切り替えてしまう」実装であれば、検証は成功してしまう。

アーキテクトとしての監査視点:
この脆弱性を防ぐには、検証ロジックを「アルゴリズムの種類」と「鍵の型」の両面から物理的に隔離する必要がある。

  • 鍵のコンテキスト分離: 鍵を管理するストアにおいて、鍵の使用目的(HMAC用かRSA用か)をメタデータとして保持し、トークンデコード時にその型が一致するかを確認するロジックを組み込むこと。
  • 型定義の厳格化: 静的型付け言語(GoやJavaなど)であれば、検証関数に渡す鍵オブジェクトの型をインターフェースレベルで厳格に分けるのがベストだ。

3. 次世代の防衛:耐量子暗号とガードレイルの設計

今後、私たちは「耐量子暗号(PQC)」への移行という大きな転換点に立つ。JWTのような署名技術も、近い将来、量子アルゴリズム(Shorのアルゴリズムなど)によるRSAやECDSAの解読リスクにさらされる。

今のうちから設計すべきは、「アルゴリズムのアップグレードパス(Agility)」だ。JWTのヘッダー処理部を切り出し、将来的に登場するポスト量子署名(Dilithium等)へシームレスに移行できるよう、ラッパー層を今の開発基盤に埋め込んでおく必要がある。

また、生成AIがアプリケーションの認証フローを攻撃対象とする「プロンプトインジェクション」の影響を考慮するなら、JWTのペイロード内に「意図的な署名範囲の限定」を設けることも検討すべきだ。AIが生成したトークンが悪用されるケースでは、トークン自体の検証だけでなく、リクエストの内容(コンテキスト)との整合性をチェックする「ガードレイル・ミドルウェア」が、最後の砦となる。

最後に:泥臭い検証の積み重ね

セキュリティは「設定」ではなく「検証」だ。

1. 静的解析(SAST): `alg`を動的に読み取る設定になっていないか、CI/CDパイプラインで自動テストする。
2. 動的解析(DAST): `none`攻撃や鍵混同攻撃を自動実行するFuzzingツールを、自社のステージング環境に組み込む。
3. 監視: 認証エラーのログを単なる「401」として捨てず、アルゴリズム不一致のパターンを収集し、組織的な攻撃キャンペーンの兆候を捉える。

技術は常に進化するが、攻撃者が突く「実装者の怠慢」という脆弱性は変わらない。常に仕様の深淵を見つめ、ライブラリのブラックボックスを疑い続けること。それが、真のセキュリティアーキテクトが担うべき責務である。

コメント

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