Upgrade to Pro — share decks privately, control downloads, hide ads and more …

認証は「誰か」までしか守らない ── オブジェクト単位の認可漏れが IDOR ──

認証は「誰か」までしか守らない ── オブジェクト単位の認可漏れが IDOR ──

【社内セキュリティ勉強会 #1 認証・認可】の登壇資料です。

認証を通過しても、オブジェクト単位で認可を確認しなければ他人のデータは見えてしまう——「認証≠認可」を手を動かせる形で持ち帰る回です。

find($id) で取ったレコードを所有チェックせず返す、よくある実装がどう IDOR(安全でない直接オブジェクト参照 / BOLA / Broken Access Control)を生むのか。
URL の ID を1つ書き換えるだけの疑似デモで穴を体感し、「直接参照+認可チェックの欠落」という設計の言葉に翻訳。
最後に FuelPHP(認可がコントローラに散る=分散・暗黙)と Laravel の Gate/Policy(1箇所に集約=集中・明示)を対比します。

▼ 持ち帰る結論
① 認証は本人確認まで
② 取得できる ≠ 見てよい
③ 認可は1箇所に明示集約

想定読者は「認証は書けるが認可は曖昧だった、ちょっと前の自分」。全6回シリーズの第1回。

Avatar for kuroneko

kuroneko

July 16, 2026

More Decks by kuroneko

Other Decks in Technology

Transcript

  1. 今日のゴール: 認証≠認可を、手を動かせる形で持ち帰る 認証を通過しても、Object単位で認可しないと他人のデータは見える(認証≠認可) 第1回 認証・認可 第2回 インジェクション /SSRF 第3回 XSS/CSRF

    /セッション 第4回 暗号/シークレット 第5回 ネットワーク/WAF 第6回 ログ/追跡 想定読者は「ちょっと前の自分」。今日は認可を中心に、認証は要点だけ扱います。 話者: kuroneko
  2. 今日の道順と、持ち帰る結論3つ 道順: 用語 → 認証の限界 → デモ(IDOR) → 設計論 →

    フレームワーク 結論① 認証は本人確認まで 結論② 取得できる≠見てよい 結論③ 認可は1箇所に明示集約
  3. 識別 → 認証 → 認可 の3段階 本人確認(認証)の先に、許可(認可)がある STEP 1 識別

    Identification 識別子を付与する STEP 2 認証 Authentication 識別子の正当性を検証 STEP 3 認可 Authorization 許可された操作だけを実行 認証が済んでも、認可は別物 ── 今日の主役は③認可
  4. 門番(Reference Monitor)が抜けると他人のデータに届く Subject →[Reference Monitor]→ Object(Operation を実行) 誰が (Subject) アクセスを試みる主体

    (ユーザー/プロセス) 門番 (Reference Monitor) Object単位で認可を確認 抜けると誰でも到達できる 何に (Object) 保護対象の資産(データ・API)
  5. 認証は本人確認まで ── 3要素で確かめる 多要素でも AiTM・MFA疲労で破られうる 要素 英語 例 知識 Something

    you know パスワード 所有 Something you have ハードウェアトークン スマートカード 生体 Something you are 指紋・顔
  6. デモで見るもの: URLのIDを1つ書き換えるだけ 参考実例: http://billpay.com/api/v1/cust/459(顧客IDをURL直接指定) STEP 1 正規ログイン 認証は通っている STEP 2

    IDを書き換え /api/users/123 の 123 だけ STEP 3 レスポンスを見る 他人のデータが出るか 注目 ── 認証は正しいのに、他人のデータが出るか?
  7. IDOR=直接参照 + 認可チェックの欠落 さっきのデモの一般化 ── 実例 http://billpay.com/api/v1/cust/459 ❖ APIの引数にオブジェクトの直接参照(ID)が使われる ❖

    そのオブジェクトにアクセス制御(認可)が課されていない ❖ 攻撃者はパラメータ値を総当たりで権限外データにアクセスできる
  8. 別名: IDOR / BOLA / Broken Access Control 用語が分裂して混乱しがちだが、根は同じ(直接参照+認可欠落) 名称

    出自 関係 IDOR 古典/Web全般で使われる名称 本体は同じ BOLA OWASP API Security Top 10 API1:2023 オブジェクト単位の認可の失敗 Broken Access Control OWASP Top 10(Web)A01:2021 (2017 5位→1位) IDOR を例として内包
  9. 認可には4種類ある: 集中 / 分散 / 暗黙 / 明示 2軸=どこで判断するか(集中 分散)×

    明示的に要求するか(暗黙 明示) ❖ この4分類が、後のフレームワーク対比の評価軸 になる 分類 意味 集中型 認可を1箇所で判断 分散型 認可を各所に散らして判断 暗黙的 認可を明示的に要求しない 明示的 認可を明示的に要求する
  10. FuelPHP: 行レベルの認可は自前実装しがち Auth パッケージ=認証+グループ/ロールベースの ACL 認可はある ❖ Auth パッケージ=認証+グループ/ロールベースの ACL

    認可はある ❖ 無いのは「これはあなたのデータか」= Object(行)単位の認可の仕組み ❖ 判断がコントローラに散る=分散・暗黙になりやすい
  11. この穴あきコードが、さっきのデモの正体です before(FuelPHP 1.x)── 所有チェックが無い ❖ get_view($id) が Model_User::find($id) して返すだ け

    ❖ 「これはあなたのものか」を確認していない= 所有チェック無し ❖ 実装全文は Appendix A5 // FuelPHP 1.x Controller_Rest public function get_view($id = null) { $user = Model_User::find($id); // ↑ ID で取得するだけ・所有未確認 return $this->response($user); // ↑ 認可チェック無し → IDOR }
  12. Policyコード例: 所有者だけが見られると宣言 明示的認可の実物 ── view(User $user, User $model): bool ❖

    UserPolicy::view(User $user, User $model): bool ❖ return $user->id === $model->id;(自分自身だけ閲 覧可) ❖ 所有リソースなら $resource->user_id === $user- >id も同じ考え // app/Policies/UserPolicy.php class UserPolicy { // 自分自身だけ閲覧可(明示的認可) public function view( User $user, User $model): bool { return $user->id === $model->id; } }
  13. RMB は「解決」だけ ── 認可は別で書く after ── RMBは解決のみ・認可は走らない。Gate::authorize を足して初めて403 ❖ Route

    Model Binding は ID→モデルを解決するだ け=認可は走らない ❖ Gate::authorize('view', $user) を足して初めて他人 なら403 ❖ ルートで守るなら ->can('view', 'user') ミドルウェ アでも可 class UserController extends Controller { public function show(User $user) { // RMB=ID解決のみ・認可は別 Gate::authorize('view', $user); return $user; // 他人なら403 } } // route: ->can('view', 'user')
  14. 対比: 分散・暗黙 vs 集中・明示 「どこで判断するか・明示するか」で穴の生まれやすさが決まる 観点 FuelPHP(1.x) Laravel(13.x) 認可の置き場所 コントローラに散る

    Policy に集約 集中-分散 分散 集中 暗黙-明示 暗黙になりやすい 明示 IDORの起きやすさ 起きやすい 起きにくい
  15. 脅威→対策: 認可の穴は「明示」で塞ぐ 全編の背骨を1枚に ── 脅威の地図の上に対策を並べ直す 脅威 対策(明示で塞ぐ) IDOR / BOLA(直接参照+認可欠落)

    Object単位の明示的認可を1箇所(Policy/共通ヘルパ)に 認証と認可の混同 認証=本人確認まで/認可=別途 Object単位で確認 暗黙的認可(取得できる=見てよい) デフォルト拒否+明示的認可の強制
  16. A1: 認証の深掘りは CySec 復習ログ #6 へ 本編で圧縮した認証の受け皿 ❖ MFA攻撃=AiTM /

    MFA疲労(キーワードのみ) ❖ NIST SP800-63 の AAL(キーワードのみ) ❖ 詳細は CySec 復習ログ #6
  17. A3: 認証の5要素(3要素+2) 本編の3要素に Somewhere you are / Something you do

    を足した5要素 要素 英語表記 例 知識 Something you know パスワード 所有 Something you have トークン・スマートカード 生体 Something you are 指紋・顔 場所 Somewhere you are 位置情報/GPS 行動 Something you do 署名・打鍵の癖
  18. A4: IDOR以外のAPI攻撃(CCT の8項目) IDOR はこの8項目の2番目(★)── この順・この表記 # API攻撃(CCT・この順) 1 ファジング

    2 安全でない直接オブジェクト参照(IDOR)★ 3 無効な入力攻撃 4 インジェクション攻撃 5 安全でないSSL設定 6 安全でないセッション/認証の処理 7 ログイン/クレデンシャルスタッフィング攻撃 8 API DDoS攻撃
  19. A5-1: FuelPHP 自前実装(before)全文 本編と同じコード ── fuel/app/.../api/users.php(FuelPHP 1.x) ❖ Controller_Rest の

    get_view($id) が find($id) して 返すだけ ❖ 認可チェックが無い=IDOR(所有未確認) ❖ レスポンス例=デモの他人データ(id:124 佐藤花 子) class Controller_Api_Users extends Controller_Rest { public function get_view($id = null) { $user = Model_User::find($id); return $this->response($user); // ↑ find だけ・所有未確認 → IDOR } }
  20. A5-2: Laravel Policy+コントローラ(after)全文 本編と同じコード ── 明示的認可(Laravel 13.x) ❖ UserPolicy::view で所有者だけ許可(明示的認可)

    ❖ コントローラで Gate::authorize('view', $user) → 他人なら403 ❖ ルートで守るなら ->can('view', 'user') // UserPolicy.php (Laravel 13.x) public function view(User $user, User $model): bool { return $user->id === $model->id; } // UserController.php public function show(User $user) { Gate::authorize('view', $user); return $user; // 他人なら403 } // route: ->can('view', 'user')
  21. A6: 用語の系譜(出典リンク集) IDOR / BOLA / Broken Access Control の出自と出典

    名称 出自 出典 IDOR 古典/Web(CCTで扱う) CCT eBook 書籍263/PDF274 BOLA OWASP API Security Top 10 API1:2023 owasp.org/API- Security/editions/2023/en/0xa1-broken- object-level-authorization/ Broken Access Control OWASP Top 10 A01:2021 owasp.org/Top10/2021/A01_2021- Broken_Access_Control/