セキュリティ・ID

IDORとは|「find({ id })」の1行が、あなたのアプリの全データを他人に見せる

IDORとは具体的にどんなバグなのかなぜ「find({ id })」の書き方が危険なのかIDを複雑にすれば安全になるのか

ブラウザのURLバーに/orders/1002と入っている。末尾の数字を1001に変えて開き直す。もし他人の注文が表示されたら——それがIDOR(Insecure Direct Object Reference)です。攻撃ツールも専門知識も要りません。数字を書き換えるだけです。

この記事は解説だけでは終わりません。脆弱なコードと修正後のコードを実際にNode.jsで実行し、その出力をそのまま載せています。修正は1行ですが、その1行の有無が「他人の注文明細」と「アクセス拒否」を分けます。

この記事の要点

  • IDORはURLやパラメータのIDを書き換えるだけで成立する。技術的難易度は低いが被害は大きい
  • 原因はコードの1行——「IDだけで検索」し「所有者を確認しない」実装
  • 修正も1行——検索条件に requesting user の ID を必ず含める
  • UUID化は推測を難しくするだけで、認可チェックの代わりにはならない

定義と、実際にコードを動かして見る挙動

IDORの本体は「アクセス制御チェックの欠落」。この記事のコードは想像ではなく実際に実行した結果で、1行の差がそのまま被害の有無になる。

認可設計とIDORの関係は、OWASPの定義に集約されています。“a vulnerability that arises when attackers can access or modify objects by manipulating identifiers”。URLやパラメータの識別子を操作するだけで、他人のオブジェクトにアクセス・変更できてしまう脆弱性です。

実際に動かして確かめる

認可設計とIDORを理解する一番の近道は、コードを動かして見ることです。

百聞は一実行に如かずです。以下のコードは編集部が実際にNode.jsで実行したもので、出力もそのまま貼っています。

idor-demo.mjs(実行済み。編集部環境で検証)javascript
const orders = [
  { id: 1001, userId: "user-alice", item: "ノートPC", amount: 128000 },
  { id: 1002, userId: "user-bob", item: "モニター", amount: 32000 },
];

// ❌ 脆弱: IDだけで検索。所有者を確認しない
function getOrderVulnerable(orderId) {
  return orders.find((o) => o.id === orderId) ?? null;
}

// ✅ 修正: 所有者(userId)も条件に含める
function getOrderFixed(orderId, requestingUserId) {
  return orders.find((o) => o.id === orderId && o.userId === requestingUserId) ?? null;
}
実行結果(そのまま。加工なし)text
=== 脆弱な実装(IDのみで検索) ===
attacker(user-mallory) が bob の注文1002を要求:
{
  "id": 1002,
  "userId": "user-bob",
  "item": "モニター",
  "amount": 32000
}

=== 修正後の実装(所有者チェックあり) ===
attacker(user-mallory) が bob の注文1002を要求:
null
bob(user-bob) が自分の注文1002を要求:
{
  "id": 1002,
  "userId": "user-bob",
  "item": "モニター",
  "amount": 32000
}

結果が語ること

同じgetOrderという関数名で、差は検索条件に userId が入っているかだけです。脆弱な方はmalloryにbobの注文明細(金額つき)をそのまま返し、修正版は他人のデータには null、自分のデータには正しく応答しています。

Insecure Direct Object Reference (IDOR) is a vulnerability that arises when attackers can access or modify objects by manipulating identifiers used in a web application's URLs or parameters.
出典OWASP Cheat Sheet Series「Insecure Direct Object Reference Prevention Cheat Sheet」 一次情報を確認2026-08-14 この内容の有効期限2027-02-14

対策は3層——検索条件・データセット・識別子

本命は検索条件への所有者ID追加。IDの複雑化とデータセット限定は、それを補強する層として重ねる。

認可設計とIDORの対策は、OWASPがいくつかの層で示しています。“Implement access control checks for each object”。ユーザーがアクセスを試みるオブジェクトごとに、アクセス制御チェックを実装する——実務では3つの層に分けて考えると設計しやすくなります。

層1(本命): 検索条件に所有者を必ず入れる

orders.js(Prismaでの実装例。findFirstに条件を足すだけ)javascript
// ❌
const order = await prisma.order.findUnique({ where: { id: orderId } });

// ✅
const order = await prisma.order.findFirst({
  where: { id: orderId, userId: req.user.id },
});

層2: 「持っているものだけ」から探す発想

OWASPはこうも助言しています。全件から検索してIDが一致するかを見るのではなく、最初からユーザーがアクセス権を持つデータセットの中だけを探す——「自分のプロジェクト一覧から取得する」のように、範囲を絞ってから検索する発想です。この考え方はAPI連携の記事で扱った最小権限の原則と同じです。

層3(補助): 推測しにくい識別子

連番ID(1, 2, 3…)は総当たりで簡単に探索できます。UUIDやランダムな長い文字列に変えると推測は難しくなりますが、OWASPは“access control is crucial even with these identifiers”と釘を刺しています。複雑な識別子はチェックの代わりではなく、チェックに重ねる保険です。

Implement access control checks for each object that users try to access
出典OWASP Cheat Sheet Series「IDOR Prevention」(対策の記載部分) 一次情報を確認2026-08-14 この内容の有効期限2027-02-14

自分のアプリで探す——15分でできる手動テスト

自動診断ツールは苦手な領域。2つのテストアカウントとIDの入れ替えだけで、多くのIDORは自分で見つけられる。

認可設計とIDORの不備は「仕様として何が正しいか」を判断する必要があるため、機械的な脆弱性スキャナーでは見つけにくい代表格です。個人開発者が自分でできる、最も確実なテスト手順を示します。

手順

  1. テストアカウントを2つ作る(A・B)
  2. Aでログインし、自分のデータ(注文・投稿・設定など)のURLやAPIレスポンスからIDを控える
  3. Bでログインした状態で、控えたAのIDを使ってアクセスを試みる(URL書き換え・APIパラメータ変更)
  4. Aのデータが見えた・編集できたら、そのエンドポイントはIDOR。修正して層1から適用する

UIの非表示は防御にならない

余談 「一覧には出ないから安全」という誤解

画面のメニューに導線がないデータでも、URLやAPIを直接叩けば取得できてしまいます。「見せていない」と「見えない」は別物です。UI上の非表示は認可の代わりにならないという点は、IDOR対策の中でも見落とされやすい落とし穴です。

この節は一次情報での裏取りが未了です。 編集部の整理(IDORの手動テスト手順)(2026-08-14 記載)

よくある質問

本当にコードを書き換えるだけで直りますか?
この記事の実演コードでは、検索条件に所有者IDを1つ加えるだけで防げています。ただし実際のアプリでは、この修正を全てのデータ取得箇所に漏れなく適用することが本当の難所です。
IDをUUIDにすれば安全になりますか?
推測しにくくなるだけで、認可チェックが不要になるわけではありません。UUIDでも、URLの共有・ログ・リファラ経由で漏れることがあり、OWASPも「複雑な識別子は多層防御の一部であり、アクセス制御そのものの代わりにはならない」と明記しています。
フレームワークを使えば自動的に防げますか?
防げません。ORMは「クエリの書き方」を助けてくれますが、「誰のデータを取るべきか」というビジネスルールはアプリ側が判断するしかありません。ここがフレームワークの守備範囲の外です。
見つけるにはどうすればいいですか?
自分のアプリで、2つの異なるユーザーでログインし、片方のIDをもう片方のリクエストに混ぜてみるのが最も確実です。自動診断ツールはこの種の論理的な不備を見つけにくいため、手動テストが有効です。

まとめ

  • IDORはURLの数字を変えるだけで成立する、技術的難易度が低く被害が大きい脆弱性
  • 原因は「IDだけで検索」する1行。修正も所有者条件を足す1行
  • ID の複雑化は補助策。認可チェックそのものの代替にはならない
  • 全データ取得箇所への機械的な徹底が唯一の対策。抜けが1箇所でもそこが穴になる

今日から始められること

  1. 自分のアプリの全データ取得コードで、検索条件に所有者IDが入っているかgrepで確認する
  2. 2つのテストアカウントで、片方のIDをもう片方のリクエストに混ぜて試す
  3. 新しいエンドポイントを作るたび、所有者チェックをテンプレート化して漏れを防ぐ
  4. IDを推測しやすい連番から、UUID等へ移行できるか検討する(補助策として)

実務で組んだ認可設計とIDORのワークフローには、値段が付きます

同じ課題を持つ会社にとって、動いている設定は「作る時間」を買えるということです。ServiceDockは自作のワークフローやテンプレートを出品できるマーケットプレイスです。手数料や出品の流れは出品者向けページにまとまっています。

出品の仕組みを見る