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 のようなヘッダー情報は、攻撃者が自由にいじれる「外部からの入力」であることを忘れないでください。「鍵は必ず静的なホワイトリストからロードする」、これだけで防げる攻撃は山ほどあります。
コードを書くときは、常に「この変数に ../../ が入ってきたらどうなる?」と自問自答してください。その疑い深さこそが、最強の防御力になります。現場のコードで不安な箇所があれば、すぐにこのホワイトリスト方式へリファクタリングしましょう。
コメント