mybook

プロローグ — ある日突然、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のグラフが真っ赤だった。

Loading diagram...

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に存在する問題をリストアップした。

Loading diagram...

リョウが発見した問題点:

  1. レート制限なし — 1秒間に何リクエストでも処理してしまう
  2. 弱いパスワードポリシー — 6文字以上なら何でも許可
  3. JWT署名検証の甘さ — アルゴリズム混在の危険
  4. 過剰なデータ返却 — 必要以上のフィールドをAPIが返す
  5. ログイン試行の無制限 — アカウントロックアウトがない
  6. CORS設定ミス — すべてのオリジンを許可していた
  7. HTTPSのみ強制なし — HTTP通信も許可していた

INFO

OWASP(Open Web Application Security Project)は、APIセキュリティの最も重大な脆弱性トップ10を「OWASP API Security Top 10」として公開しています。今回の攻撃はその複数の項目に該当します。

経営陣への報告

翌朝9時、リョウはCTOのアキラに状況を報告した。

「どれくらいのデータが漏洩したんだ?」

「最悪のケースで顧客5社分の在庫データです。合計で約23万件のレコード」

「コンプライアンス的には?」

「個人情報保護法の観点から、報告義務が発生する可能性があります」

重い沈黙が続いた。

「リョウ、今すぐ全部直してくれ。予算は後で考える」

セキュリティ強化の旅が始まる

この日を境に、リョウのAPIセキュリティ強化プロジェクトが始まった。

Loading diagram...

これから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)
end

WARNING

JWTシークレットのローテーションは全ユーザーを強制ログアウトさせます。インシデント対応時の緊急手段として有効ですが、ユーザーへの事前通知が必要な場合もあります。

この本で学ぶこと

リョウと一緒に、以下のセキュリティ対策を実装していこう。

テーマ対象の脅威
第2章HTTPS/TLS通信の盗聴・改ざん
第3章認証の基礎不正アクセス
第4章OAuth 2.0/OIDC認可の欠陥
第5章JWTトークン偽造
第6章入力バリデーションインジェクション攻撃
第7章レート制限/DDoS対策サービス停止攻撃
第8章データ暗号化データ漏洩
第9章ロギング/監査インシデント追跡
第10章CORSクロスサイト攻撃
第11章セキュリティテスト脆弱性の自動発見
第12章継続的改善長期的なセキュリティ

セキュリティは一度実装して終わりではない。リョウが学んだように、攻撃者は常に新しい方法を試みてくる。この本を読み終えた頃には、あなた自身がリョウのように、APIを守る力を身につけているはずだ。

では、第1の教訓から始めよう。すべての通信を暗号化することから。