セキュリティ システムアーキテクチャ

Web脆弱性の基礎知識(第3回):設計不備・API・ビジネスロジック

2026年08月24日 1 分で読めます 24 ビュー

Web脆弱性の基礎知識(第3回):設計不備・API・ビジネスロジック——スキャナーが見つけられない欠陥

前の2回で扱ったのは、コードの1行を指させる種類の誤りでした。文字列連結で組み立てたクエリ、認可チェックを忘れたエンドポイント、本番に残った初期設定の認証情報です。今回はもっと厄介です。コードは書いたとおりに動き、設定も整い、スキャナーは何も報告しない——それでも最初の発想そのものが誤っている。請求先メールアドレスを再認証なしで変更できる設計が危険だと、ツールは教えてくれません。

OWASP はこの領域を Insecure Design と呼び、繰り返す価値のある指摘をしています。設計の欠陥は、きれいなコードを書くことでは修正できない、ということです。今日のプロダクトが実質的に Web・モバイル・パートナーを同時に支える API の集合である以上、リスクの大半はブラウザを離れ、API 層とその背後の業務フローへ移動しました。

本記事は、文法の学習段階を終えた開発者、そして QA と PM に向けたものです。実務ではロジックの欠陥を最初に見つけるのは、たいていセキュリティ担当ではなく彼らです。

2025-mappings.pngOWASP Top 10:2021 と OWASP Top 10:2025 の対応関係。出典:OWASP Foundation(CC BY-SA 4.0)。

1. Insecure Design(A06):ホワイトボードで生まれる脆弱性

実装バグは「正しくやろうとして書き損じた」ものであり、設計不備は「その状況を一度も考えなかった」ものです。誰でも調べられる情報に依存したパスワード再設定、保持数に上限のない予約機能、販売者が自分の取引を自ら承認できる返金フロー、1人が千回使ったらどうなるかを誰も問わなかったクーポン——いずれも典型例です。

対策

脅威モデリングはリリース直前ではなく、フローを描いている段階で行います。「誰がこれを悪用したいか、何を得るか、どの経路を試すか」という3つの問いに1時間を使うほうが、数週間のコードレビューより効果的です。ユーザーストーリーの隣に「攻撃者は Y のために X を試みる」という悪用ストーリーを書き、セキュリティ要件を受け入れ基準に直接記載してください。金銭・個人データ・管理権限に関わるフローは再利用可能な設計パターンとして標準化し、あらゆるリソースに回数・数量・有効期限の上限を必ず設けます。

2. BOLA:API 規模になった IDOR

OWASP API Security Top 10 の第1位は Broken Object Level Authorization で、これは第2回の IDOR が装いを変えたものです。異なるのは影響範囲で、1つの内部 API が Web・モバイル・キオスク・パートナー連携を同時に支えるため、所有者チェックを忘れた1つのエンドポイントがすべての経路にデータを開いてしまいます。GraphQL では1つのクエリが複数の関連を横断する一方、権限確認が最外層でしか行われないため、さらに危険です。

対策は、認可チェックをコントローラではなくデータに最も近い層(リポジトリやサービス)へ置き、すべてのチャネルで同じ関数を再利用すること。GraphQL では各リゾルバで確認します。UUID のような推測困難な識別子は有用ですが、それは難読化であってアクセス制御ではなく、所有者チェックの代替にはなりません。

3. 受け取りすぎと返しすぎ

マスアサインメントは、リクエストボディ全体をモデルに束縛し、ユーザーが付け足した権限フラグや支払い済みフラグをそのまま保存してしまう問題です。その鏡像が過剰なレスポンスで、画面は3項目しか表示しなくても、電話番号・住所・社内メモが応答にそのまま含まれ、ネットワークタブを開けば誰でも読めます。

対策:エンドポイントごとに受け入れる項目を明示的に許可リスト化し、入力専用オブジェクトと出力専用シリアライザを用意して DB モデルを直接公開しない。OpenAPI 定義を契約とみなし、契約外の項目が応答に含まれないことを自動テストで検証する。データの隠蔽をフロントエンドに任せてはいけません。

4. リソース消費の無制限:金額で測る脆弱性

レート制限のない SMS ワンタイムコード送信は、攻撃者に開けたままの蛇口です。10万件を一度に返す検索 API、深くネストした GraphQL クエリ、同期実行のレポート出力、リクエストごとに課金される AI モデル呼び出しも同様です。

対策:ユーザー単位・IP 単位・エンドポイント単位のレート制限を設け、高コスト処理にはより厳しい閾値を適用する。ページネーション上限はサーバー側で強制し、GraphQL の深さと複雑度を制限する。外部呼び出しにはタイムアウトとサーキットブレーカーを設定し、重い処理は割当付きのキューへ回す。急激な費用の増加は悪用の最初の兆候であるため、コストをセキュリティ指標として監視してください。

5. ビジネスロジックの不備と数ミリ秒の競合

ここは優秀なペネトレーションテスターが価値を出し、自動ツールが無力になる領域です。クライアントから送られた価格をサーバーが信じる、多段フローを飛ばして未払いのまま確認画面に到達する、同じ注文を二重に返金する、そして残高確認が一度きりのうちに20件の同時リクエストを撃ち込む競合状態などが典型です。

対策:金額はすべてサーバーで再計算し、ワークフローの状態は各ステップ後にサーバー側で保持・検証する。残高減算には DB レベルのロックか適切な分離レベルを用い、最後の砦としてユニーク制約を置く。取引作成 API には冪等性キーを用意し、再送で重複が生まれないようにする。テスト側では、同一リソースに対して意図的に並列リクエストを撃つこと。実施しているチームは少なく、そしてほぼ必ず何かが見つかります。

6. 自社ページで動く他人のコード

決済ページは広告・解析・チャットウィジェット・公開 CDN のライブラリまで読み込みます。それらはすべて自社コードと同じ権限で動作し、フォームも DOM も読めます。カード情報のスキミング事件はこの経路で起きます。攻撃者は被害企業のサーバーに侵入せず、皆が読み込む第三者ファイルを書き換えるだけです。

対策:機微なページのスクリプト数を最小化し、重要なライブラリは自社でホストする。外部スクリプトには Subresource Integrity を用い、Content-Security-Policy で許可元を制限し、第三者ウィジェットは sandbox 付き iframe に隔離し、postMessage の受信時は必ず origin を検証します。

7. 1つのシステムに多数の顧客

SaaS で最も高くつく問いは、顧客 A のデータが顧客 B の画面に漏れないかどうかです。原因は執拗な攻撃者よりも、テナント条件を書き忘れた集計クエリや、システム権限で走るバッチであることがほとんどです。

対策:テナント分離は ORM の既定スコープや DB の行レベルセキュリティなど、可能な限り低い層で強制する。テナント識別子はリクエストパラメータではなくセッションコンテキストから取得する。他テナントのデータを読めないことを自動テストで保証し、権限が集中する社内管理画面こそ堅牢にし、ログと分析基盤では個人データをマスクします。

設計確定時のチェックリスト

  • 「誰がどう悪用するか」を声に出して検討した。
  • オブジェクトの読み書きすべてでサーバー側の所有者チェックがある。
  • 受け入れる入力項目と返す出力項目を明示的に定義した。
  • 高コストのエンドポイントにレート制限・割当・タイムアウトがある。
  • 金額とワークフロー状態をサーバーで計算・検証している。
  • 残高変更処理が並列実行から保護されている。
  • 機微なページの第三者スクリプトを棚卸しし、SRI と CSP を適用した。
  • テナント間でデータが越境しないことを自動テストで証明している。

まとめ

今回の脆弱性はスキャナーのレポートには現れません。現れるのは設計会議の議事録——あるいはその会議が開かれなかったという事実の中です。幸いなことに、これは最も安く直せる種類でもあります。紙の上にあるうちに直せば、の話ですが。最終回となる第4回では、これらの原則を運用の習慣へ変える方法——CI に組み込むセキュリティテスト、期限を伴う脆弱性管理、そして実際に事故が起きた日の動き方を扱います。

参考文献

ビジネス変革の準備はできていますか?

AIとデジタル変革の活用について、ぜひご相談ください。

記事をシェア