プロローグ — ある日突然、APIが攻撃された
午前2時17分。リョウのスマートフォンが激しく振動した。
[CRITICAL] API Error Rate: 94.3% (threshold: 5%)
[CRITICAL] Database connections: 1000/1000 (EXHAUSTED)
[CRITICAL] Response time: 32,847ms (threshold: 2000ms)
リョウ・ナカムラ、28歳。スタートアップ「StockFlow」のシニアエンジニアだ。在庫管理SaaSのバックエンドAPIを一人で守っている。画面を見た瞬間、全身から血の気が引いた。
まずい。本番がダウンしている。
インシデント発生の瞬間
リョウは急いでラップトップを開き、AWSコンソールにアクセスした。CloudWatchのグラフが真っ赤だった。
APIエンドポイントに対して、毎秒1000件以上のリクエストが叩き込まれていた。レート制限は存在しなかった。データベース接続プールは完全に枯渇し、正規のユーザーは誰もサービスを使えない状態になっていた。
リョウはSlackに書き込んだ。
@channel 本番障害発生中。現在調査中。
ログを追う
CloudTrailとアプリケーションログを見ていくと、攻撃のパターンが見えてきた。
# 攻撃ログの抜粋(1秒間)
2024-01-15 02:15:00.001 POST /api/v1/auth/login - IP: 203.0.113.42
2024-01-15 02:15:00.002 POST /api/v1/auth/login - IP: 203.0.113.42
2024-01-15 02:15:00.003 POST /api/v1/auth/login - IP: 203.0.113.43
2024-01-15 02:15:00.004 POST /api/v1/auth/login - IP: 198.51.100.71
# ... 999件続くログイン認証エンドポイントへのブルートフォース攻撃だった。しかも複数のIPアドレスから分散して行われていたため、単純なIP制限では防げない。
WARNING
ブルートフォース攻撃は、正しいパスワードを見つけるまで大量の試行を繰り返す攻撃です。レート制限がない場合、攻撃者は理論上無制限に試行できます。
さらに深刻だったのは、攻撃が成功していたことだ。
# 成功したログインのログ
2024-01-15 02:15:03.847 POST /api/v1/auth/login - Status: 200 - user_id: 4821
2024-01-15 02:15:04.123 GET /api/v1/inventory - user_id: 4821 - records: 15000
2024-01-15 02:15:04.456 GET /api/v1/inventory/export - user_id: 4821 - size: 8.3MB顧客の在庫データが抜き取られていた。
脆弱性の全体像
夜明けまでかけて調査したリョウは、自分のAPIに存在する問題をリストアップした。
リョウが発見した問題点:
- レート制限なし — 1秒間に何リクエストでも処理してしまう
- 弱いパスワードポリシー — 6文字以上なら何でも許可
- JWT署名検証の甘さ — アルゴリズム混在の危険
- 過剰なデータ返却 — 必要以上のフィールドをAPIが返す
- ログイン試行の無制限 — アカウントロックアウトがない
- CORS設定ミス — すべてのオリジンを許可していた
- HTTPSのみ強制なし — HTTP通信も許可していた
INFO
OWASP(Open Web Application Security Project)は、APIセキュリティの最も重大な脆弱性トップ10を「OWASP API Security Top 10」として公開しています。今回の攻撃はその複数の項目に該当します。
経営陣への報告
翌朝9時、リョウはCTOのアキラに状況を報告した。
「どれくらいのデータが漏洩したんだ?」
「最悪のケースで顧客5社分の在庫データです。合計で約23万件のレコード」
「コンプライアンス的には?」
「個人情報保護法の観点から、報告義務が発生する可能性があります」
重い沈黙が続いた。
「リョウ、今すぐ全部直してくれ。予算は後で考える」
セキュリティ強化の旅が始まる
この日を境に、リョウのAPIセキュリティ強化プロジェクトが始まった。
これから12章にわたって、リョウが実装していくセキュリティ対策を追っていこう。単なる技術的な実装だけでなく、「なぜその対策が必要なのか」という攻撃者の視点を常に意識しながら。
まず最初にやったこと
リョウが最初に取った緊急措置は3つだった。
# 1. AWS WAFでIPレートベースルールを設定
aws wafv2 create-rate-based-rule \
--name "EmergencyRateLimit" \
--scope REGIONAL \
--rate-limit 100 \
--aggregate-key-type IP
# 2. 攻撃元IPのブロック
aws wafv2 create-ip-set \
--name "BlockedIPs" \
--scope REGIONAL \
--ip-address-version IPV4 \
--addresses "203.0.113.0/24" "198.51.100.0/24"
# 3. 全ユーザーのセッションを強制失効# config/initializers/emergency_session_invalidation.rb
# 全ユーザーのJWTを無効化するため、シークレットキーをローテーション
Rails.application.config.before_initialize do
# JWT secret の即時ローテーション(既存トークンが全て無効に)
ENV['JWT_SECRET'] = SecureRandom.hex(64)
endWARNING
JWTシークレットのローテーションは全ユーザーを強制ログアウトさせます。インシデント対応時の緊急手段として有効ですが、ユーザーへの事前通知が必要な場合もあります。
この本で学ぶこと
リョウと一緒に、以下のセキュリティ対策を実装していこう。
| 章 | テーマ | 対象の脅威 |
|---|---|---|
| 第2章 | HTTPS/TLS | 通信の盗聴・改ざん |
| 第3章 | 認証の基礎 | 不正アクセス |
| 第4章 | OAuth 2.0/OIDC | 認可の欠陥 |
| 第5章 | JWT | トークン偽造 |
| 第6章 | 入力バリデーション | インジェクション攻撃 |
| 第7章 | レート制限/DDoS対策 | サービス停止攻撃 |
| 第8章 | データ暗号化 | データ漏洩 |
| 第9章 | ロギング/監査 | インシデント追跡 |
| 第10章 | CORS | クロスサイト攻撃 |
| 第11章 | セキュリティテスト | 脆弱性の自動発見 |
| 第12章 | 継続的改善 | 長期的なセキュリティ |
セキュリティは一度実装して終わりではない。リョウが学んだように、攻撃者は常に新しい方法を試みてくる。この本を読み終えた頃には、あなた自身がリョウのように、APIを守る力を身につけているはずだ。
では、第1の教訓から始めよう。すべての通信を暗号化することから。