認可の脆弱性、実例 — URLの数字一つが全顧客データを開いてしまう
IDORは自動スキャナーが最も捕まえにくい代表的な脆弱性です。実際のパターンとコードレベルでの防ぎ方。
URLの/orders/1042を/orders/1043に変えただけなのに他人の注文履歴が見えてしまうなら、それは偶然ではなく認可(Authorization)の脆弱性です。業界ではIDOR(Insecure Direct Object Reference)と呼ばれ、自動スキャナーが最も捕まえにくい脆弱性の一つです。
認証と認可は違う
ログインできるかどうか(認証、Authentication)と、ログインした人がそのデータを見る資格があるか(認可、Authorization)はまったく別の問題です。多くのサービスはログインは丁寧に作りますが、「このユーザーがこのリソースの所有者か」は見落としがちです。
実際にはこう突破される
GET /api/orders/1042 → 自分の注文(正常) GET /api/orders/1043 → 他人の注文がそのまま見える(脆弱)
サーバーコードがおおよそこうなっているときに発生します。
// 脆弱なコード: idだけで検索し、所有者確認がない
app.get('/api/orders/:id', async (req, res) => {
const order = await db.order.findUnique({ where: { id: req.params.id } });
res.json(order);
});ログインしているかどうかだけを確認し、「この注文がログインユーザーのものか」は確認していません。数字のIDを順番に変えていくだけで、全顧客の注文を一通り見ることができます。
なぜスキャナーが見落としやすいのか
一般的な自動スキャナーは「このAPIが200を返すか」「SQLインジェクションが可能か」といったパターンはよく捕まえます。しかしIDORは正常に200と正しいデータを返すため、そのデータが「誰のものか」というビジネス上の文脈を理解しなければ問題に気づけません。これは単純なパターンマッチングより一段深い分析が必要です。
直し方
// 修正: 検索条件に必ず所有者(userId)を含める
app.get('/api/orders/:id', async (req, res) => {
const order = await db.order.findFirst({
where: { id: req.params.id, userId: req.user.id },
});
if (!order) return res.status(404).end();
res.json(order);
});原則は一つです。「検索条件には常に現在ログインしているユーザーを含める」。 IDだけで検索しているクエリがあれば、すべて疑ってください。
テストの仕方
最も確実な方法は、アカウントを2つ作り、Aアカウントでログインしたまま、Bアカウントのリソースidを直接リクエストしてみることです。200と正常なデータが返ってきたら問題があります。404か403が返るのが正常です。
IDORには派手なハッキング技術は必要ありません。ブラウザのアドレスバーで数字を一つ変えるだけで発見され、悪用されます。だからこそ、より危険なのです。
WhiteHat Codeのディープスキャンは、コードのビジネス上の文脈も合わせて分析し、こうした認可漏れのパターンを見つけ出します。無料スキャンで今すぐ確認してみてください。