APIは「性善説」で動いていない:ゲートウェイとWAFで防御する「リソース枯渇」の防衛線
現場のエンジニア諸君、お疲れ様。今日はAPIのセキュリティ、特に「レート制限」と「WAFによる防御」について、教科書には載っていない泥臭い話をしよう。
多くの開発者がAPIを作るとき、機能実装に忙殺されて「誰が、どのくらいの頻度で叩いてくるか」を忘れがちだ。だが、現実は甘くない。君たちが書いたAPIは、リリースしたその瞬間に、世界中のボットネットから「食い物」としてスキャンされている。
今日は、APIを守るための防衛線をどう構築するか、実践的な話をしよう。
—
1. なぜ「レート制限」が必要なのか?(PoC的視点)
攻撃者が狙うのは、単なるデータの窃取だけではない。「DoS(サービス拒否)攻撃」によるリソース枯渇だ。
例えば、DBのJOINを多用する重い検索APIがあるとしよう。攻撃者は次のようなスクリプトを数百台のノードから同時に叩き込む。
攻撃者の視点:リソースを枯渇させるための単純な並列スキャン
import requests
import threading
def attack():
while True:
# 認証不要の重いAPIエンドポイントを狙い撃ち
requests.get(“https://api.example.com/v1/search?query=a”)
for _ in range(50):
threading.Thread(target=attack).start()
これをやられると、DBのコネクションプールは一瞬で埋まり、他の正規ユーザーが「503 Service Unavailable」を拝むことになる。これこそが、APIゲートウェイでのスロットリング(レート制限)が不可欠な理由だ。
—
2. APIゲートウェイ(Nginx)でのレート制限設定
APIゲートウェイやリバースプロキシのレベルで、IPアドレス単位の制限をかけるのが最初の防壁だ。Nginxを使っているなら、limit_req モジュールを適切に設定する。
Nginx設定ファイル: レート制限の定義
1分間に20回まで許可(burstは一時的なバーストを許容する数)
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=20r/m;
server {
location /api/ {
# 制限超過時は503を返す
limit_req zone=api_limit burst=5 nodelay;
proxy_pass http://backend_upstream;
}
}
ポイント: burstを設定しないと、正規ユーザーが少し速くクリックしただけでエラーが出る。UXを損なわない絶妙な値を見つけるのが腕の見せ所だ。
—
3. AWS WAFによる「レートベース」の防御
クラウド環境なら、アプリケーションの手前でAWS WAFを噛ませるのが定石だ。ここで重要なのは「レートベースのルール」だ。
AWS WAFのレートベースルール設定の考え方はこうだ。
- 評価スコープ: 特定のAPIパス(例:
/v1/loginや/v1/search)に限定する。 - レート閾値: 「5分間で100リクエスト」など、平常時の最大トラフィックの2〜3倍程度に設定する。
- アクション: 最初は「カウントのみ」でログを分析し、誤検知がないことを確認してから「ブロック」に切り替える。
Terraformでの設定例(抜粋)
resource “aws_wafv2_web_acl” “api_acl” {
name = “api-security-acl”
scope = “REGIONAL”
rule {
name = “RateLimitRule”
priority = 1
statement {
rate_based_statement {
limit = 1000 # 5分間で1000回まで
aggregate_key_type = “IP”
}
}
action {
block {} # 閾値を超えたら即ブロック
}
visibility_config {
cloudwatch_metrics_enabled = true
metric_name = “RateLimitMetric”
sampled_requests_enabled = true
}
}
}
—
4. 現場で生き残るための「心構え」
ここまで実装すれば安心……と言いたいところだが、一つだけ注意点がある。「攻撃者は動的IPを使いこなす」という点だ。
単一のIPをブロックするだけでは、ボットネットは数十万のIPをローテーションして攻撃を継続する。だからこそ、以下の2段構えが必須になる。
1. 異常検知: ユーザーエージェントの偏りや、APIの叩き方の「不自然な規則性」をCloudWatch Logs等で監視する。
2. 認証の強制: そもそも、重要なエンドポイントにはAPIキーやJWTによる認証を必須とし、認証後のユーザーID単位でレート制限をかける。
Node.js (Express) でのユーザー単位レート制限の例
const rateLimit = require(“express-rate-limit”);
const apiLimiter = rateLimit({
windowMs: 15 60 1000, // 15分
max: 100, // ユーザーIDごとに100回
keyGenerator: (req) => req.user.id, // IPではなくユーザーIDで制限
message: “レート制限を超過しました。しばらく待ってください。”
});
app.use(“/api/v1/protected”, apiLimiter, (req, res) => {
// 処理ロジック
});
—
まとめ:セキュリティは「多層」で戦え
APIゲートウェイ、WAF、そしてアプリケーションコード。これらを組み合わせることで初めて「堅牢」と言える。
「自分たちのサービスは小さいから狙われない」と思っているなら、今すぐその考えを捨てろ。攻撃者は君たちのサービスそのものよりも、脆弱なAPIを「踏み台」にして他の攻撃へ繋げることを狙っているんだ。
まずはWAFのログを眺めるところから始めてみよう。そこには、君たちの想像を超えた「悪意あるトラフィック」の姿が刻まれているはずだ。
守りを固めることは、開発者の責任だ。健闘を祈る。
コメント