【実務・中級編】OIDCにおけるUserInfoエンドポイントの安全な実装 – アプリケーションセキュリティ & 安全な開発防御ガイド

OIDCのUserInfoエンドポイントは「ただのデータ提供口」ではない。君が落としがちな盲点と守り方

現場でコードレビューをしていると、OpenID Connect (OIDC) の実装で「認証さえ通れば、UserInfoエンドポイントはアクセストークンを投げれば誰でも属性を返していい」と勘違いしているケースによく出くわす。

はっきり言おう。それは脆弱性の宝庫だ。

UserInfoエンドポイントは、Identity Provider (IdP) において最も機密性の高いPII(個人識別情報)を吐き出す蛇口だ。ここを甘く設計することは、玄関の鍵を閉めておきながら、裏口の窓を全開にしているようなものだ。今回は、この「安全なUserInfo実装」という、一見地味だが死活的に重要なテーマについて、泥臭い実務の視点から解説する。

1. 攻撃者がUserInfoを狙う理由:PII漏洩の連鎖

攻撃者は、なぜわざわざUserInfoエンドポイントを狙うのか? 理由はシンプルだ。「横展開(Lateral Movement)」のトリガーになるからだ。

例えば、特定のWebアプリから漏洩したアクセストークンを攻撃者が手に入れたとする。もし、そのトークンがUserInfoエンドポイントで「別のユーザー」の属性も取得できるようなガバガバな認可設定になっていれば、芋づる式に全ユーザーのメールアドレス、電話番号、住所が引き抜かれる。

PoC:攻撃者が行う「IDの詰め替え」攻撃

攻撃者は、入手した `access_token` を使い、リクエストパラメータを細工して他のユーザーの情報を覗き見ようとする。

攻撃者のリクエスト例
GET /userinfo HTTP/1.1
Host: idp.example.com
Authorization: Bearer <盗んだアクセストークン>
ここで独自拡張パラメータなどを悪用し、他のIDの取得を試みる
?user_id=target_user_001

もしサーバー側が「トークン内のサブジェクト(sub)とリクエストされたID」を照合していなければ、認証済みユーザーの権限を悪用して、他人のプロフィールをすべてダンプできる。

2. セキュアな実装:Python (FastAPI) での防衛策

UserInfoエンドポイントで絶対に守るべき鉄則は、「アクセストークンに紐づくユーザー情報のみを返し、リクエストパラメータによるID指定を信用しない」ことだ。

以下は、FastAPIを用いたセキュアなUserInfoエンドポイントの実装例だ。

from fastapi import FastAPI, Depends, HTTPException, Security
from fastapi.security import HTTPBearer

security = HTTPBearer()

模擬的なデータベース検索関数
def get_user_from_token(token: str):
# 1. トークンの検証(JWT署名検証などはミドルウェアで済ませている前提)
# 2. トークン内の ‘sub’ クレームを取り出す
sub = decode_and_verify_token(token)

# 3. データベースから該当するユーザー情報のみを抽出
# ★重要: ここで引数に外部からの入力(user_id)を直接使わない!
user = db.query(User).filter(User.id == sub).first()
return user

@app.get(“/userinfo”)
async def userinfo(token: str = Security(security)):
user = get_user_from_token(token.credentials)

if not user:
raise HTTPException(status_code=404, detail=”User not found”)

# PIIの最小化:必要な属性のみを返す
return {
“sub”: user.id,
“email”: user.email,
“name”: user.full_name
}

ポイント:

  • `user_id` をURLパラメータ等で受け取る設計自体を排除する。
  • トークンのデコード結果(`sub`)のみを信頼する。これが認可の「真実のソース(Source of Truth)」だ。

3. インフラ側での「二重の盾」:Nginx/WAFの設定

コードが万が一ミスを犯した時のために、インフラ層でもガードを固めておく。UserInfoエンドポイントへの不正なリクエストをブロックする設定だ。

Nginxによるレート制限(ブルートフォース対策)

UserInfoエンドポイントに対する大量のIDスキャンを防ぐために、レート制限は必須だ。

nginx.conf
limit_req_zone $binary_remote_addr zone=userinfo_limit:10m rate=5r/s;

location /userinfo {
limit_req zone=userinfo_limit burst=10 nodelay;
# 認証情報を伴わないリクエストは即座に拒否
auth_request /auth_check;

}

4. 最後に:エンジニアが持つべき「防御的思考」

今回紹介した実装は、教科書に載っているような当たり前のことに見えるかもしれない。しかし、インシデント現場では「開発者が気を利かせて実装したバックドア的なパラメータ」が原因で情報漏洩が起きることが非常に多い。

  • PIIの最小化: そもそもUserInfoで住所や電話番号を返す必要があるのか?`scope` を細かく分けて、必要な時以外は返さない設計にせよ。
  • ログの監視: UserInfoエンドポイントへの403エラーが急増していないか?それは攻撃者が脆弱性を探っているサインだ。

セキュリティは「完璧な製品」を導入して終わるものではない。君が書くその一行、その条件分岐の一つひとつに、「悪意ある第三者ならどうやって突破するか?」という視点を組み込むこと。それが、真のエンジニアとしての矜持だ。

次のリリースでは、君のUserInfoエンドポイントが「ただ動くだけ」ではなく「圧倒的に堅牢」であることを期待している。何かあれば、またいつでも相談してくれ。

コメント

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