図面IDを替えても越境させない、取引先ポータルの認可クエリ
取引先向け図面ポータルで、URLの図面IDを替えると別会社の図面を取得できる。製造DX責任者とWeb開発責任者向けに、契約・品番・改訂を一度に判定する認可クエリと回帰試験を示します。
ログイン済みでも図面ごとの認可は必要
対象は、自社が管理し、検証を許可したlocal・test・staging環境です。オーナーはポータルのプロダクト責任者とバックエンド責任者。入力は、取引先ID、契約、割当品番、図面ID、改訂、公開状態、操作種別です。
問題の流れは、GET /api/drawings/{id}/downloadが受け取ったIDで全図面から一件を探し、ストレージキーを返す実装です。画面の一覧を自社分へ絞っても、サーバーが対象図面との関係を再判定しなければ、利用者が管理するIDが認可を迂回します。MITREはこの弱点をCWE-639として整理し、各データアクセスで十分な権限を確かめるよう求めています。[1]
UUID化は推測を難しくする補助策ですが、漏れたリンクやログからIDを得た場合の認可にはなりません。OWASPも、識別子の複雑さにかかわらず、オブジェクト単位のアクセス検査が必要だとしています。[2]
全件検索を「見える集合」からの検索へ替える
修正前は、外部入力の図面IDだけで検索します。
NG: drawing = drawings.findById(request.path.id)
修正後は、セッションから得た取引先IDを使い、その取引先に見える集合へ先に絞ります。リクエスト本文のsupplier_idは信用しません。
OK: drawing = drawings.findOne({
drawing_id: request.path.id,
supplier_id: session.supplier_id,
part_assignment: "active",
contract_status: "active",
revision_status: "released",
action: "download"
})
if drawing == none: return 404
audit(actor, drawing_id, action, decision, policy_version)
ここで大切なのは、図面テーブルだけでなく、取引先―契約―割当品番―公開改訂の関係を同じ判定に入れることです。旧版の参照が必要な社内品質担当と、最新の承認済み改訂だけを受け取る加工委託先は、同じ「ログイン済み」でも許可が異なります。
stagingで再現し、順番に塞ぐ
- 試験データを作る。 取引先Aへ品番P-100・改訂C、取引先BへP-200・改訂Dを割り当てます。同じ権限ロールの利用者A/Bを用意し、実在図面や本番資格情報は使いません。
- 入口を棚卸しする。 詳細表示だけでなく、ダウンロード、プレビュー、検索、一覧、ZIP出力、更新、削除を列挙します。OWASPは読み取りだけでなく、作成・更新・削除・出力など全操作で確認するよう勧めています。[2]
- 認可サービスを一つにする。
can(actor, resource, action)の入力を固定し、各入口は必ずこの判定かスコープ付きリポジトリを通します。オブジェクトストレージの署名URLは、認可成功後に短時間だけ発行します。 - 拒否を同じ形にする。 存在しないIDと、他社のIDは外部へ同じ404を返します。内部ログには拒否理由、取引先、操作、ポリシー版を残します。
- CIへ回帰試験を入れる。 Actor・Resource・Actionの表から許可/拒否を生成し、変更時に必ず実行します。OWASPも、同一ロールの別利用者で読み取り・更新・削除を再試行する「Multi-User Replay」を示しています。[3]
【仮想例】取引先Aの担当者が自社のP-100改訂Cを開いた後、stagingで図面IDだけを取引先BのP-200へ置き換えます。旧実装は200とファイルを返しました。改修後は、取引先IDをセッションから取得し、割当品番と公開改訂を含む検索が空になるため404を返します。完了条件は、Aの許可図面だけが取得でき、Bの図面名・改訂・ファイル容量も応答へ出ず、拒否ログが一件残ることです。
誤拒否を避ける展開と戻し方
最初は新旧判定をstagingで並走させ、差分を「許可→拒否」「拒否→許可」に分けます。本番では監査のみ、ダウンロード、プレビュー、検索・ZIP出力の順に適用範囲を広げます。拒否率、取引先別404急増、署名URL発行数、ポリシー版ごとの差分を監視します。
誤拒否が業務を止めた場合は、認可そのものを無効にせず、直前の承認済みポリシー版へ戻します。新規契約割当の反映遅延なら、契約同期を止めて手動承認キューへ退避します。全社共通図面だけを無認証で公開するという場合には向きません。その場合も、公開対象を別ストアへ分け、取引先限定図面と同じ経路に混ぜない設計が必要です。
明日から使える認可回帰表
| Actor | Resource | Action | 合格条件 |
|---|---|---|---|
| 取引先A | Aに割当済み・公開改訂 | 表示/取得 | 200、監査ログあり |
| 取引先A | Bに割当済み図面 | 表示/取得 | 404、本文にメタ情報なし |
| 取引先A | Aの未公開改訂 | 表示/取得 | 404 |
| 取引先A | 契約終了後の図面 | 表示/取得 | 404 |
| 未認証 | 任意の図面 | 表示/取得 | 401 |
| 社内品質担当 | 許可された旧版 | 表示 | 200、職務権限を記録 |
合格は、許可ケースが業務どおり動き、拒否ケースがすべてファイルとメタ情報を返さず、全入口で同じポリシー版が記録されることです。新しい図面機能を追加したら、この表へ行を加えることを受け入れ条件にします。
参考資料
- MITRE CWE-639: Authorization Bypass Through User-Controlled Key
- OWASP Cheat Sheet Series: Insecure Direct Object Reference Prevention
- OWASP Cheat Sheet Series: Authorization Regression Testing
本記事はAIを活用して作成し、公開前に自動レビューを実施しています。情報の確認日:2026-10-26。
Author
ELYSAI編集部