【実務・中級編】 JWTのkid(Key ID)パラメータ注入による署名検証回避 – オフェンシブセキュリティ & リバースエンジニアリング防御ガイド

JWTの「kid」パラメータ注入:認証の「鍵穴」をすり替える手口と防衛術

JWT(JSON Web Token)を扱う際、多くの開発者が「署名検証をしているから安全だ」と過信しています。しかし、その「検証」のプロセスに、攻撃者が入り込む巨大な隙間があることをご存知でしょうか。

今日は、JWTのヘッダーにある kid (Key ID) パラメータを悪用して、サーバー上の任意のファイルを鍵として強制的に読み込ませる「鍵のすり替え攻撃」について深掘りします。これは単なる理論上の脆弱性ではなく、現場で最も恐ろしい「認証の無効化」に直結するテクニックです。

—

1. なぜ「kid」が攻撃の標的になるのか

JWTのヘッダーには、署名検証に必要な鍵を特定するための kid パラメータが含まれることがあります。サーバー側は通常、この kid を見て「ああ、この鍵IDならこの公開鍵を使えばいいんだな」と、データベースやファイルシステムから鍵をロードします。

問題は、「サーバーが指定された kid を無条件に信頼してファイルパスを構成している場合」に発生します。

攻撃者は、この kid に ../../etc/passwd のようなディレクトリトラバーサル文字列を仕込みます。サーバーが運悪くこのパスをロードし、攻撃者が用意した署名と「ファイルの先頭部分(または特定文字列)」を照合して検証を通してしまうと、認証は完全に突破されます。

2. 攻撃のシナリオ:鍵のすり替え PoC

例えば、以下のような脆弱な検証ロジックを想定してください。

# 危険な実装例(検証用)
def verify_jwt(token, kid):
    # ユーザー入力のkidをそのままファイルパスに使用している
    key_path = f"/var/www/keys/{kid}.pem"
    with open(key_path, "r") as f:
        public_key = f.read()
    # この公開鍵を使って署名検証を行う...(以下省略)

攻撃者は、kid に ../../../dev/null を指定し、署名(Signature)部分を空、あるいはサーバーが「公開鍵の一部」と見なすようなデータで偽造します。署名アルゴリズムに none を指定したり、鍵のロードに失敗した際の例外処理が甘い場合、認証ロジックは無効化されます。

—

3. 【防御】セキュアな実装のための鉄則

この攻撃を完全に防ぐには、「外部からの入力を、直接パスの構成要素にしない」という原則を徹底することです。

対策A:ホワイトリストによる厳格な管理

鍵のIDをファイルパスに直結させず、事前に許可されたIDのみをマップ(辞書)で管理します。

# 安全な実装例(Python)
ALLOWED_KEYS = {
    "key-001": "/etc/ssl/keys/app_public.pem",
    "key-002": "/etc/ssl/keys/backup_public.pem"
}

def get_public_key(kid):
    # 直接パスを連結せず、ホワイトリストから取得
    if kid not in ALLOWED_KEYS:
        raise ValueError("無効な鍵IDです")
    return ALLOWED_KEYS[kid]

対策B:JWSライブラリの設定

多くのライブラリ(PyJWT, node-jsonwebtoken等)は、デフォルトで kid の検証を厳格に行うオプションがあります。信頼できない kid を受け付けないように設定してください。

// Node.js (jsonwebtoken) での検証例
jwt.verify(token, getKey, { algorithms: ['RS256'] }, (err, decoded) => {
  // getKey関数内でホワイトリスト検証を実装すること
});

—

4. インフラ・ミドルウェア層での補強

コードレベルの修正に加え、WAFやサーバー設定でも多層防御を敷くのがプロの仕事です。

  • Nginx設定でのパス制限: アプリケーションサーバーに届く前に、kid を含むリクエストパラメータを正規化し、../ が含まれる場合は即座に403を返すルールを記述します。
  • IAM / 権限分離: 鍵ファイルを保存しているディレクトリには、Webサーバーの実行ユーザー(www-data等)以外がアクセスできないよう、物理的に読み取り権限を制限します。
# Nginx設定例:悪意あるトラバーサル文字列をブロック
location / {
    if ($arg_kid ~* "\.\./") {
        return 403;
    }
    proxy_pass http://backend_app;
}

—

セキュリティチーフからのアドバイス

「JWTは便利だが、実装者の直感が最も裏切られやすい場所」です。

特に kid のようなヘッダー情報は、攻撃者が自由にいじれる「外部からの入力」であることを忘れないでください。「鍵は必ず静的なホワイトリストからロードする」、これだけで防げる攻撃は山ほどあります。

コードを書くときは、常に「この変数に ../../ が入ってきたらどうなる?」と自問自答してください。その疑い深さこそが、最強の防御力になります。現場のコードで不安な箇所があれば、すぐにこのホワイトリスト方式へリファクタリングしましょう。

コメント

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