【実務・中級編】OpenID ConnectのIDトークン検証におけるaud(Audience)とiss(Issuer)の確認 – アプリケーションセキュリティ & 安全な開発防御ガイド

IDトークン検証の「盲点」:audとissをサボる開発者が招く破滅

現場でコードレビューをしていると、未だに「とりあえずJWTをデコードして、中身のsub(ユーザーID)だけ見て認証を通す」という実装に出くわす。これ、セキュリティの観点から言えば「鍵のかかっていない玄関のマットの下に合鍵を置いている」のと同じだ。

OpenID Connect (OIDC) におけるIDトークンは、単なるユーザー情報の運び屋ではない。IDプロバイダー(IdP)とあなたのアプリケーションの間の「信頼の契約書」だ。この契約書に記載された iss(発行者)と aud(宛先)を無視することは、攻撃者に偽造トークンを送り込まれるためのレッドカーペットを敷いているに等しい。

なぜ aud と iss の検証が「生と死」を分けるのか

攻撃シナリオを想像してみてほしい。あなたのアプリがGoogleのIdPを利用しているとしよう。

1. 攻撃者も同じIdPを利用してアカウントを作成する。
2. 攻撃者は、自分自身のためにIdPから有効なIDトークンを取得する。
3. 攻撃者は、そのトークンをあなたのアプリのログインエンドポイントに投げ込む。

もしあなたのアプリが iss(発行元が本当にGoogleか?)と aud(このトークンは、あなたのアプリに向けて発行されたものか?)をチェックしていなければどうなるか。あなたのアプリは「おっ、Googleから来た有効なトークンだね!じゃあこの攻撃者をユーザーとしてログインさせよう」と判断し、攻撃者はあなたのアプリで他人のふりをしてログインできてしまう。

これが「Audience混同攻撃」の古典的な手口だ。インフラをいくら強固にしても、アプリ層でこの扉を解放していたら意味がない。

—

実践:セキュアな検証実装(Python / PyJWT)

多くの開発者はライブラリに頼るが、「ライブラリが検証してくれるだろう」という思い込みが最も危ない。 ライブラリのオプションを正しく指定することが、プロの仕事だ。

ここではPythonの PyJWT を使った、現場でそのまま使えるセキュアな検証コードを示す。

import jwt
from jwt import PyJWKClient

IdPから公開鍵セット(JWKS)を取得するURL
jwks_client = PyJWKClient(“https://accounts.google.com/.well-known/openid-configuration”)

def verify_id_token(token, expected_client_id, expected_issuer):
try:
# 公開鍵を取得
signing_key = jwks_client.get_signing_key_from_jwt(token)

# 検証の要:ここでissとaudを厳密にチェックする
payload = jwt.decode(
token,
signing_key.key,
algorithms=[“RS256”],
audience=expected_client_id, # ここでaudを検証
issuer=expected_issuer # ここでissを検証
)
return payload
except jwt.exceptions.InvalidAudienceError:
print(“警告: 不正なAudienceです。攻撃の可能性があります。”)
except jwt.exceptions.InvalidIssuerError:
print(“警告: 信頼できない発行者からのトークンです。”)
except Exception as e:
print(f”検証失敗: {e}”)
return None

使用例
expected_client_id = “あなたのアプリのクライアントID”
expected_issuer = “https://accounts.google.com”

ここがポイント

  • audience=expected_client_id: ライブラリに期待するクライアントIDを明示的に渡す。これがないと、他アプリ向けに発行されたトークンを素通りさせることになる。
  • issuer=expected_issuer: 悪意のあるIdPが発行したトークンを弾くための防波堤。
  • algorithms=["RS256"]: アルゴリズムを固定する。これを指定しないと、攻撃者がヘッダーのアルゴリズムを none に書き換えて署名を無効化する「None-Algorithm攻撃」を受ける可能性がある。

—

インフラ層での防御:WAFや設定でできること

アプリ層の修正が完了したら、最後に守りを固める。IDトークンの不正利用を検知するため、WAFやリバースプロキシで「異常なリクエスト」を弾く設定も検討しよう。

例えば、Nginxで特定の異常なJWTヘッダーを弾く、あるいはWAFで「IDトークンに含まれるclaimの構造」を検査することは、多層防御の観点から非常に有効だ。

AWS WAFでの考え方:
Web ACLを使って、特定のパス(ログイン処理)に対して、リクエストボディ内の特定の文字列パターンを検査するルールを作成する。IDトークンはJWT形式(xxxxx.yyyyy.zzzzz)であるため、明らかな不正フォーマットや、ペイロードのサイズが異常に大きいリクエストは、トークン検証の前段でブロックする。

—

最後に:セキュリティは「性悪説」で設計せよ

「自分のアプリはまだ小さいから狙われない」という考えは、サイバー攻撃者にとって最も都合の良いカモだ。自動化されたボットは、脆弱性があるかどうかを秒単位でスキャンしている。

aud と iss の検証は、認証というシステムの心臓部を守るための、最低限の礼儀だ。もし今のコードでこの検証が漏れているなら、最優先でバックログに積んでくれ。リリースしてから泣くのは、君自身と、君が守るべきユーザーなのだから。

何か不明点があれば、またいつでも聞いてくれ。コードの堅牢化は、泥臭い確認作業の積み重ねだ。それができるエンジニアこそ、真のプロフェッショナルだと思う。

コメント

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