IDORの悪夢:URLの数値をいじるだけで「他人の人生」を覗き見る脆弱性の正体
現場でペネトレーションテストを行っていると、いまだに驚くほど高い確率で遭遇するのが IDOR(Insecure Direct Object Reference:安全でない直接オブジェクト参照) です。
「うちは認証もしているし、複雑なパスワードも要求しているから大丈夫」と思っている開発者は多い。しかし、IDORは認証の「先」にある、認可(Authorization)の致命的な欠陥です。ログインさえしていれば、あとはURLの user_id や invoice_id を1つずつインクリメントするだけで、他人の個人情報や機密資料がダダ漏れになる。これは、いわば「鍵のかかった家の玄関を突破したのに、中の部屋がすべて施錠なしで開けっ放し」の状態です。
今日は、この「古典的だが極めて危険」なIDORを撲滅するための実戦的な防御策を、コードレベルで叩き込みます。
—
1. IDORの PoC:攻撃者はどうやって獲物を狙うのか
攻撃者はツールを使いません。ブラウザのデベロッパーツールと、少しの「好奇心」があれば十分です。
例えば、あるECサイトの領収書表示機能が以下のようなURLだとします。
https://example.com/api/v1/invoices/9521
攻撃者の手順はこうです。
1. 自分の領収書にアクセスし、レスポンスを確認する。
2. URLの末尾を 9520 に書き換えて再読み込みする。
3. 成功すれば、他人の領収書(氏名、住所、購入履歴)が手に入る。
4. あとは 9500 から 9600 までスクリプトで一気にリクエストを投げれば、数秒でDBが丸裸です。
このとき、サーバー側で「今ログインしているユーザーが、その invoice_id を持つ権利があるか」を判定していないことが、この脆弱性の本質的な原因です。
—
2. 対策の基本:認可(Authorization)をコードに埋め込む
「IDを推測不能なUUIDにする(例: 550e8400-e29b-41d4-a716-446655440000)」という対策も一時的な難読化には有効ですが、根本解決にはなりません。正しい解決策は、「誰がそのリソースを所有しているか」をサーバーサイドで厳格に照合することです。
実装例:Python (FastAPI) による認可チェック
リクエストを受け取った際、データベースのクエリに必ず「ログインユーザーのID」を条件として含めます。
from fastapi import Depends, HTTPException, status
from sqlalchemy.orm import Session
# 脆弱なコード:IDだけで検索してしまう
# invoice = db.query(Invoice).filter(Invoice.id == invoice_id).first()
# セキュアな実装:ユーザーIDを条件に加える
def get_invoice_secure(invoice_id: int, current_user: User, db: Session):
invoice = db.query(Invoice).filter(
Invoice.id == invoice_id,
Invoice.owner_id == current_user.id # ここで所有者チェックを強制する
).first()
if not invoice:
# 存在しない場合や他人のIDの場合は、権限不足または404を返す
# 攻撃者に「IDが存在するか」を教えないために404を返すのが一般的
raise HTTPException(status_code=404, detail="Resource not found")
return invoice
—
3. インフラ・アーキテクチャによる多層防御
アプリケーションコードの修正が基本ですが、設計ミスを補完するためにWAFやアクセスコントロール層での対策も重要です。
Nginxでのアクセス制限(参考例)
特定のパスへのアクセスを、特定のロールや条件に絞るための設定です。ただし、これはあくまで補助的な手段であることを忘れないでください。
location /api/v1/invoices/ {
# 認可ロジックをバックエンドに持たせるのが大前提ですが、
# ゲートウェイ側で特定のIPや認証済みヘッダーの存在をチェックする
auth_request /auth_check;
proxy_pass http://backend_cluster;
}
—
4. 現場のシニアエンジニアからの提言
IDORを防ぐために、明日からチームで徹底すべきルールをまとめました。
- 「ID=権限」と考える: APIエンドポイントの設計時、URLに含まれるIDを処理する前に「これは本当に本人か?」という問いを必ずレビューのチェックリストに入れてください。
- 権限確認の共通化:
get_resource_by_owner(id, user_id)のようなラッパー関数を共通モジュールとして作成し、開発者が直接DBクエリを叩くのを禁止するのも非常に有効な統制です。 - テストコードでIDORを叩く: ユニットテストやE2Eテストにおいて、「ユーザーAのセッションで、ユーザーBのリソースにアクセスを試みるテストケース」を必ず含めてください。これが自動化されているチームは、圧倒的に堅牢です。
IDORは、少しの注意深さと「他人のデータを読み取ろうとする悪意」を想像する力があれば、必ず防げます。システムを開発することは、ユーザーの人生を預かることです。その重みを、コードの1行1行に込めてください。
もし、今運用しているシステムで心当たりがあるなら、今すぐコードのクエリ条件を確認してください。それが、あなたのキャリアを守る一歩になります。
コメント