securitywebai
認可(IDOR)防止チェックリスト|AI実装で壊れやすい境界を30分で見直す
|
6 min read
ログインがあるのに情報が抜ける原因はほぼ「認可」。AI生成コードで頻発するIDOR(オブジェクト単位の認可ミス)を、30分で潰すための実務チェックリストと判断基準をまとめる。
目次
- 結論(Conclusion)
- 背景(Explanation)
- 実務手順(Practical Guide)
- Step 0(3分):漏れたら致命的なオブジェクトを3つ選ぶ
- Step 1(5分):id を受け取る入口を全部洗い出す
- Step 2(8分):READで境界(owner/tenant)が強制されているか
- Step 3(8分):WRITEも同じ境界で守れているか
- Step 4(4分):一覧/検索が横断漏れしていないか
- Step 5(2分):境界の否定テストを1本入れる
- 失敗パターン(Pitfalls)
- チェックリスト(Checklist)
- FAQ
- Q1. ルートにauthが付いていれば十分?
- Q2. 403と404はどちらが正しい?
- Q3. 再発を一番減らす方法は?
- 内部リンク(Internal links)
- 参考(一次情報)
IDOR(認可ミス)を防ぐには、どこを30分で見直せばいい?
結論(Conclusion)
30分でIDORリスクを大きく下げるなら、見るべきは3つ。
- IDの入口(URL/Body/Query)を列挙し、改竄前提で扱う
- READの境界条件に
owner_id/tenant_idが入っている(id = :idだけにしない) - WRITEの境界条件も同じ(UPDATE/DELETEが owner/tenant でスコープされている)
最後に回帰テストを1本入れる。
- 「BのユーザーがAのオブジェクトを読めない/書けない」
これで再発率がガクっと下がる。RBAC/ABACはその後でいい。
Implementation examples may be available on DevSnips.
背景(Explanation)
認証は「誰か」を決める。 認可は「どのオブジェクトに触れていいか」を決める。
IDORは、ログイン済みでも id を変えるだけで他人のデータに触れる状態。
AI生成コードで増えるのは、生成パターンがこうなりがちだから。
- ルートにauthは付く
- でもクエリは
WHERE id = :id - owner/tenant境界が抜ける
UUIDは推測耐性であって、アクセス制御ではない。必要なのはサーバー側の認可。
実務手順(Practical Guide)
Step 0(3分):漏れたら致命的なオブジェクトを3つ選ぶ
例:
- 課金:invoices, subscriptions
- 業務データ:projects, documents, tickets
- 管理:members, roles, invites
判断基準:
- 漏れたら報告義務/信用毀損になるものは対象。
Step 1(5分):id を受け取る入口を全部洗い出す
探す:
GET /api/<object>/:idPATCH /api/<object>/:idDELETE /api/<object>/:idPOST /api/<object>/update(bodyにid)
payload内も検索:
id,userId,orgId,tenantId,projectId
判断基準:
- クライアントが送れる値は、必ず改竄される前提で設計する。
Step 2(8分):READで境界(owner/tenant)が強制されているか
正しい形:
- シングルでも:
WHERE owner_id = current_user_id AND id = :id - マルチなら:
WHERE tenant_id = current_tenant_id AND id = :id
アンチパターン:
findById(id)だけWHERE id = :idだけ- 境界をbodyから取る(
tenant_id = body.tenantId)
Step 3(8分):WRITEも同じ境界で守れているか
READだけ守っても事故る。
- UPDATE/DELETE のWHEREに
owner_id/tenant_id - 更新件数0なら403/404(方針に従う)
アンチパターン:
- READは守るが、WRITEは
WHERE id = :id - トランザクション外の二段階チェック(TOCTOU)
Step 4(4分):一覧/検索が横断漏れしていないか
例:
GET /api/<object>?q=...GET /api/<object>/recent
確認:
- すべてのクエリに owner/tenant が入る仕組みか
- 共通ヘルパーやORMスコープがあるか
Step 5(2分):境界の否定テストを1本入れる
最低限:
- BがAのオブジェクトを読めない
- BがAのオブジェクトを書けない
判断基準:
- テストが書けないなら、境界が散っていて再発しやすい。
失敗パターン(Pitfalls)
- UIで隠したから安全だと思う
- クライアントの
tenantId/orgIdを信用する - 403/404がバラバラで抜け穴が生まれる
- webhook/cron/batchの経路を忘れる
- ログにpayloadを出して二次漏洩
チェックリスト(Checklist)
- [ ] 致命的なオブジェクト3種が列挙できている
- [ ] ID入口(URL/body/query)が棚卸しできている
- [ ] READが owner/tenant 境界でスコープされている
- [ ] WRITEも同じ境界でスコープされている
- [ ] クライアント提供の境界値(tenantId/orgId)を信用していない
- [ ] 403/404の方針が統一されている
- [ ] 一覧/検索が owner/tenant で必ず絞られる
- [ ] 共通ヘルパー/スコープがある
- [ ] 否定テスト(BがAに触れない)がある
- [ ] denyログがある(PII/秘密は出さない)
- [ ] AI生成コードで認可に触れる変更はレビューゲートを通す
FAQ
Q1. ルートにauthが付いていれば十分?
不十分。authは「ログイン済みか」を見るだけ。IDORは「どのオブジェクトに触れていいか」なので、ハンドラ/データ層で境界を強制する必要がある。
Q2. 403と404はどちらが正しい?
どちらもあり。列挙耐性なら404、監査なら403。重要なのは“全体で統一”すること。
Q3. 再発を一番減らす方法は?
オブジェクトごとに否定テストを1本。「BがAのオブジェクトに触れない」をCIで固定する。
内部リンク(Internal links)
- 親記事(Hub):AI開発:まずはここから
- 関連記事:
参考(一次情報)
- OWASP Top 10 (A01: Broken Access Control): https://owasp.org/www-project-top-ten/