BOLA(Broken Object Level Authorization)を討つ:APIセキュリティの「盲点」を突く実戦的防衛術
やあ。現場で泥水をすすりながらインシデント対応をしてきた経験から言わせてもらうと、セキュリティの境界線は、ファイアウォールやWAFといった「外壁」ではなく、「データに触れる瞬間のロジック」そのものにある。
今日のテーマは、今まさに多くのモダンなWeb APIを蝕んでいるBOLA(Broken Object Level Authorization:オブジェクトレベルの認可不備)だ。これは単なるバグではなく、設計思想の欠陥だ。IDを書き換えるだけで他人の個人情報が丸見えになる――そんな脆弱性を、二度と自社のプロダクトに混入させないための「防衛の極意」を伝授しよう。
—
1. なぜBOLAは「侵入検知」をすり抜けるのか
BOLAの恐ろしさは、それが「正当な認証済みユーザーによる正規のAPIリクエスト」に見える点にある。
攻撃者は、ログイン後に手に入る有効なアクセストークンを手に、REST APIのパラメータをいじるだけだ。
GET /api/v1/orders/1234
これを
GET /api/v1/orders/1235
に変える。これが通ってしまったら、それは認証(あなたは誰か?)は通っているが、認可(あなたはそのリソースに触れる権利があるか?)が欠落している証拠だ。WAFは「正常な形式のAPIリクエスト」としか判断できない。
—
2. 脆弱な実装と、その「正解」
多くのエンジニアが陥る罠は、「DBからレコードが取得できればOK」という実装だ。
脆弱な実装例(Node.js / Express)
// 危険:IDがリクエストに含まれているだけで、持ち主の確認をしていない
app.get('/api/v1/orders/:id', authenticateToken, async (req, res) => {
const order = await db.orders.findByPk(req.params.id); // ここで所有者チェックが抜けている!
if (!order) return res.status(404).send();
res.json(order);
});
これでは、req.params.idを変えるだけで誰の注文履歴でも閲覧できてしまう。
防衛のためのセキュアな実装例
認可の鉄則は、「検索クエリに所有者IDを常に組み込むこと」だ。
// 安全:WHERE句で「誰のデータか」を必ず縛る
app.get('/api/v1/orders/:id', authenticateToken, async (req, res) => {
const order = await db.orders.findOne({
where: {
id: req.params.id,
userId: req.user.id // トークンから取得した「本人」のデータしか許可しない
}
});
if (!order) {
// 存在しないのか、権限がないのかを攻撃者に悟らせないため、一律404を返す
return res.status(404).send('Not Found');
}
res.json(order);
});
—
3. APIゲートウェイでの「二重の盾」
コードレベルでの防衛は必須だが、開発者のミスをカバーするために、インフラ側でも防御層を設けるべきだ。APIゲートウェイ(Kong, AWS API Gateway, Nginx等)で、リクエストの正当性を検証する仕組みを取り入れよう。
Nginxで特定のパスへのアクセスを制御する簡易的な例を挙げる。
# Nginxで特定のリクエストに対して、Luaスクリプト等を介して認可チェックを行うイメージ
location /api/v1/orders/ {
# 認可サービスへリクエストを転送し、検証を行う(auth_requestモジュール)
auth_request /auth_verify;
proxy_pass http://backend_api;
}
location = /auth_verify {
internal;
# 実際の認可判断を行う内部サービスへ飛ばす
proxy_pass http://auth_service/check-permission;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
}
—
4. 現場で生き残るための「設計ルール」3か条
教科書的な知識は忘れてもいいが、この3つだけはコードを書くときに守ってほしい。
1. 連番IDを外部公開しない: order/1234 のような連番は推測を容易にする。UUID v4やハッシュ化されたIDを使うことで、総当たり攻撃のコストを跳ね上げろ。
2. 「本人確認」をビジネスロジックの最上段に置く: データベースからデータを引っ張る前に、「このリクエストを投げる資格がこのユーザーにあるか?」を判定するミドルウェアを必ず通せ。
3. 認可のテストを自動化せよ: ユニットテストで「別ユーザーのIDを投げたら404(または403)が返るか」をテストケースに含めろ。CI/CDのパイプラインにこれが組み込まれていないなら、それは時限爆弾を抱えてデプロイしているのと同じだ。
—
最後に:プロのエンジニアであるために
BOLAは、技術的なスキルの欠如というよりは、「どうせ誰もそんな面倒なことはしないだろう」という慢心から生まれる。攻撃者は、我々が「まさか」と思う場所を必ず突いてくる。
コードをコミットする前に、一度画面の向こう側にいる攻撃者の視点に立ってみてほしい。「このID、書き換えたら何が見えるだろう?」と。その問いこそが、君のプロダクトを、そして君自身を最強のエンジニアにするための第一歩だ。
何か実装で迷ったら、またいつでも聞いてくれ。現場からは以上だ。
コメント