社内向けAPIを設計・レビューしているエンジニアや、ヘルスケア・金融系のマイクロサービスで患者データ・口座情報を扱うアーキテクトに向けた内容です。認可(authorization、ユーザーが特定のリソースにアクセスしてよいかを判定する仕組み)まわりの設計ミスは、脆弱性診断でもコードレビューでも見逃されやすいという特徴があります。その代表例が BOLA(Broken Object Level Authorization、オブジェクト単位の認可制御漏れ)です。この記事では、BOLA がなぜ発生し、自分のプロジェクトで該当するかをどう確認し、どう直すかを整理します。
何が起きるか:認証は通るのに認可だけ抜け落ちる
BOLA は、ログイン自体は正規のユーザーなのに、他人のデータに届いてしまう不具合です。
たとえば /api/prescriptions/1234 というエンドポイントがあるとします。自分の処方箋IDが 1233 のユーザーが、URLの数字を 1234 に書き換えてリクエストを送ります。
サーバー側が「トークンが有効かどうか」しか見ておらず、「そのトークンの持ち主が1234番のリソースにアクセスしてよいか」を確認していなければ、他人の処方データがそのまま返ってきます。
認証(authentication、本人確認)は通過しているため、WAF(Web Application Firewall)やAPIゲートウェイのレート制限、OAuth 2.0のスコープチェックといった一般的な防御層を素通りしてしまう点が厄介です。影響範囲は、該当エンドポイントの数字・UUIDを連番や推測可能な値で試した全パターンに及びます。1件の漏洩ではなく、スクリプトで全件スキャンされる被害になり得ます。
なぜ起きるか:設計レビューの抜け漏れを段階的に見る
BOLAは単なる実装ミスというより、設計プロセスの構造的な欠陥として発生することが多い脆弱性です。段階を追って見ていきます。
段階1:認証と認可を同じものだと扱ってしまう
JWT(JSON Web Token)やセッションを検証するミドルウェアが「ログイン済みであること」だけをチェックし、「このユーザーがこのリソースの所有者か」までは見ていないケースです。認証ロジックと認可ロジックは本来別レイヤーですが、実装上は同じガード関数に混ざりがちです。
段階2:脅威モデリングがスプリント速度と衝突する
伝統的な脅威モデリング(threat modeling、設計段階で想定される攻撃シナリオを洗い出す作業)は、数週間かけた大規模レビューとして実施されることがあります。スプリント単位で進むアジャイル開発では、このレビューがボトルネックになり、開発者がレビューを省略して納期を優先してしまう構造があります。
段階3:DFD(データフロー図)とトラストバウンダリが文書化されていない
クライアント、APIゲートウェイ、マイクロサービス、データ層、外部B2B連携(たとえば外部薬局APIのような第三者システム)の間には、それぞれ信頼境界(trust boundary)があります。この境界ごとに「誰が何を検証するか」が明文化されていないと、個々のサービスが「認可チェックは手前の層でやっているはず」と思い込み、結果として誰もチェックしていない空白地帯ができます。
自分のプロジェクトが該当するか確認する
実際にコードベースを確認する手順です。
- リソースIDをパスパラメータやクエリパラメータで受け取るエンドポイント一覧を洗い出す(例:
/api/users/{id},/api/orders?orderId=) - 該当エンドポイントのハンドラー内で「リクエスト元ユーザーのID」と「取得対象リソースの所有者ID」を突き合わせるコードがあるかを目視確認する
- ORM(Object-Relational Mapping)のクエリが
WHERE id = :idだけで、AND owner_id = :current_user_idのような所有者条件を含んでいないか確認する - IDが連番(auto increment)かUUIDv4かを確認する。連番なら総当たりの難易度が低い
- 監査ログ(audit log)にリソースアクセス時のユーザーIDとリソースIDが記録されているか確認する
コードベースでの簡易チェックとしては、以下のようなgrep検索も有効です。
# パスパラメータでIDを受け取っているルート定義を抽出
grep -rn "req.params.id\|:id\b" ./src/routes
# 所有者チェックのキーワードが近傍にあるか目視で確認
grep -rn "owner_id\|user_id ==\|current_user" ./src/controllersヒットしたルートの中で、所有者チェックのキーワードが一切出てこないファイルがあれば、そこがBOLAの候補地点です。
対策の手順
1. ミニSTRIDEゲートをバックログ整備に組み込む
STRIDE(Spoofing/Tampering/Repudiation/Information Disclosure/Denial of Service/Elevation of Privilege、6種類の脅威分類)を使った15〜20分の短い検討会を、以下の条件に当てはまるユーザーストーリーに限定して実施します。
- 患者情報・個人情報(PHI/PII)を扱う変更
- 新しい公開APIエンドポイントの追加
- 認証・認可・セッション管理ロジックの変更
- 外部サービスやWebhookとの連携追加
全てのストーリーに適用すると負荷が高すぎるため、トリガー条件を絞るのが実務上のポイントです。
2. 認可チェックをデータ層に強制する
アプリケーション層での if 文チェックだけに頼らず、データベースクエリ自体に所有者条件を組み込みます。PostgreSQLであれば Row Level Security(RLS)機能を使い、テーブル単位で「このユーザーIDの行しか見えない」というポリシーをDB側で強制できます。アプリコードの書き漏れがあっても、DB側が最終防波堤になります。
3. トラストバウンダリごとにDFDを書き、責任分界点を明記する
クライアント→APIゲートウェイ→マイクロサービス→データ層→外部連携、という各境界で「ここでは何を検証し、何は検証しない」をドキュメント化します。これにより「前段でチェック済みのはず」という思い込みの空白を防げます。
4. 自動テストにIDOR/BOLAケースを追加する
ユーザーAのトークンでユーザーBのリソースIDにアクセスするテストケースを、CI(継続的インテグレーション)に組み込みます。レスポンスが403または404になることをアサートし、回帰を機械的に検知します。
# 例: ユーザーAのトークンで他人のリソースにアクセスするテスト
curl -H "Authorization: Bearer $USER_A_TOKEN" \
https://api.example.com/prescriptions/9999
# 期待値: 403 Forbidden または 404 Not Found5. 監査ログを改ざん不可能な形で残す
アクセス記録を書き込み専用(append-only)のログストアに送ることで、後から「誰がいつ何を見たか」を追跡可能にします。これはSTRIDEの「Repudiation(否認)」対策にも直結します。
まとめ
BOLAは認証と認可を混同したまま設計を進めると発生しやすい、構造的な見落としです。
確認の第一歩は、リソースIDを受け取るエンドポイントを洗い出し、所有者チェックのコードがあるかをgrepと目視で確認することです。
対策としては、負荷の高い全件レビューではなく、トリガー条件を絞ったミニSTRIDEゲートをスプリントに組み込み、DB側のRow Level Securityとテスト自動化で二重の防波堤を作る形が現実的です。
次のスプリント計画の際に、新しいAPIエンドポイントや認可ロジック変更が含まれるストーリーを1件選び、15分のミニSTRIDE検討を試してみるところから始められます。