こんにちは!セキュリティの世界へようこそ。
新人のIT担当者や、これからWeb開発やセキュリティの基礎をしっかり学びたいと考えている皆さん、日々の業務お疲れ様です。
突然ですが、皆さんの身の回りにある「鍵」や「防犯」について少し想像してみてください。
例えば、家の玄関の鍵。これは家族以外の人が勝手に入ってこないようにするための大切な仕組みですよね。でも、もし「合鍵を何枚も作って、次々に違う人間がドアをガチャガチャと開けようとしたらどうなるでしょうか?」あるいは、「1人の人間が、変装して何度も何度もインターホンを鳴らし続けたらどうなるでしょうか?」
実は、Webの世界でもこれと全く同じことが起きています。それが今回お話しする「APIのレート制限(Rate Limiting)」と、それを何とかすり抜けようとする攻撃、そしてシステムを守るための防衛策です。
難しそうに聞こえる用語も、身近な例えを交えながら一歩ずつ丁寧に紐解いていきますので、どうぞリラックスして最後まで読んでみてくださいね!
—
1. APIの「レート制限」ってなに?(お家に例えてみよう)
現代のWebアプリケーションやスマートフォンアプリは、裏側で「API(エーピーアイ)」と呼ばれる仕組みを使ってデータをやり取りしています。例えば、天気予報アプリがサーバーに「今日の東京の天気は?」と聞きに行き、サーバーがそれを答える、といった具合です。
ここで、サーバー側にとって頭が痛い問題が起きることがあります。それが「荒らし(ボット)」や「悪意ある大量アクセス」です。
身近な例え:人気ラーメン店の「お替わりルール」
想像してみてください。あなたが人気ラーメン店の店長だとします。
「1人につき、お水は1杯まで」というルール(制限)を作っておけば、お客さんは落ち着いてラーメンを食べてくれますよね。これが「レート制限」です。
もしこのルールがなかったらどうでしょう?
1人の悪意あるお客さんが、水差しを独占して何百杯も水を要求し続けたら、他のお客さんに水が行き渡らなくなってしまいますよね。さらに、店員さんもその対応だけに追われて、お店全体が機能しなくなってしまいます。これが、Webの世界でいう「DoS(サービス妨害)攻撃」や「APIの過負荷」という状態です。
サーバーを守るために「同じ人からのアクセスは、1分間に60回まで!」といった制限をかけることが、APIにおけるレート制限の基本なんです。
—
2. 攻撃者はどうやって制限を「すり抜ける」のか?
さて、ここからが少しダークなお話。セキュリティを破る側(レッドチームや攻撃者)は、この「1人につき何回まで」というルールをどうやって突破しようとするのでしょうか?
先ほどのラーメン店の例に戻りましょう。
店長が「顔を覚えて、同じやつには水を与えないぞ!」と対策したとします。すると狡猾な泥棒は次のような手口を使います。
1. お面を次々とかぶり直して別人になりすます
→ ネットの世界では、これを「IPローテーション(IPアドレスの切り替え)」と呼びます。アクセス元の住所(IPアドレス)をコロコロ変えて、別人のふりをしてアクセスし続けます。
2. 変装グッズや偽の身分証を使う
→ ネットの世界では、これを「ヘッダー偽装」と呼びます。サーバーに対して「私はさっきとは違うブラウザですよ」「違うデバイスから来ましたよ」という嘘の情報を伝えます。
これらを組み合わせることで、攻撃者は「私は同じ人じゃありません、色々な場所から来た普通のたくさんのお客さんです!」とサーバーに思い込ませ、制限を華麗にすり抜けようと試みるのです。
—
3. 実践!脆弱なAPIと攻撃の仕組みを見てみよう
百聞は一見に如かず。実際に、簡単なPHPのコードを使って「レート制限が甘いAPI」と、それがどうやって狙われるのかを覗いてみましょう。
脆弱なAPIのサンプルコード
以下のコードは、送られてきたIPアドレスだけを見て制限をかけようとしている、少し脇の甘いAPIのイメージです。
<?php
// 簡易的なAPIのエンドポイント(PHPの例)
// 注意:このコードは学習用のサンプルであり、実運用ではセキュリティ上の懸念があります。
// 訪問者のIPアドレスを取得
$clientIp = $_SERVER['REMOTE_ADDR'];
// 簡易的なアクセス回数の記録(実際はRedisやDBを使います)
$cacheFile = __DIR__ . '/counter_' . md5($clientIp) . '.txt';
$accessCount = file_exists($cacheFile) ? (int)file_get_contents($cacheFile) : 0;
// 1分間に5回までという制限
if ($accessCount >= 5) {
http_response_code(429); // 429 Too Many Requests
echo json_encode(["error" => "リクエストが多すぎます。しばらく待ってください。"]);
exit;
}
// カウントを増やす
$accessCount++;
file_put_contents($cacheFile, $accessCount);
// 正常なレスポンスを返す
echo json_encode(["status" => "success", "message" => "データを正常に取得しました。"]);
?>
攻撃者が仕掛ける「すり抜け」のからくり
上記のコードでは $clientIp(IPアドレス)だけで判定しています。しかし、攻撃者は次のようなスクリプト(Pythonなど)を使い、リクエストごとに偽装ヘッダー(X-Forwarded-For など)を書き換えて送信してきます。
# 攻撃者が使用するスクリプトの概念図(Python)
import requests
import random
url = "http://example.com/api.php"
for i in range(100):
# 毎回、違う偽のIPアドレスをヘッダーに仕込む
fake_ip = f"192.168.1.{random.randint(1, 255)}"
headers = {
"X-Forwarded-For": fake_ip,
"User-Agent": "Mozilla/5.0 (EvilBot)"
}
response = requests.get(url, headers=headers)
print(f"試行 {i+1}: IP={fake_ip} -> ステータスコード: {response.status_code}")
もしサーバーやリバースプロキシ(NginxやCloudflareなど)が、この偽装された X-Forwarded-For ヘッダーを無条件で信頼してしまうと、サーバーは「お、さっきとは違う人だな!」と勘違いし、無限にアクセスを通してしまうことになります。これがレート制限回避のメカニズムです。
—
4. 開発者・インフラ担当者が行うべき「本当の防御策」
「うわ、じゃあIPアドレスもヘッダーも信用できないじゃないか!どうすればいいんだよ……」と不安になったかもしれませんが、安心してください。私たちには強力な防衛手段があります。一歩ずつ堅牢な仕組みを作っていきましょう。
① 信頼できるプロキシ経由のIPのみを信用する
アプリケーションが直接インターネットに公開されているのではなく、手前にロードバランサーやCDN(CloudflareやAWS CloudFrontなど)を挟んでいる場合、本当のクライアントIPは特定の信頼できるヘッダーに格納されます。
フロントエンドのサーバー以外から送られてきた X-Forwarded-For などのヘッダーは、一切信用せず上書き・または無視する設定を必ず行いましょう。
② IPアドレスだけに頼らない「マルチファクター制限」
IPアドレスは簡単に偽装・変更されてしまいます。そのため、レート制限を行う際は以下の要素を複合的に組み合わせるのが現代のベストプラクティスです。
- APIトークン / 認証情報(JWTやセッション):
未ログインのアクセスだけでなく、ログイン中のユーザーID単位で制限をかける。
- デバイスフィンガープリンティング:
ブラウザの種類、画面解像度、言語設定などを組み合わせて「同一のデバイス」を特定する。
- CAPTCHA(人間証明)の導入:
怪しい挙動(短時間に大量のリクエストなど)を検知した瞬間だけ、「私はロボットではありません」のパズルをポップアップさせる。
③ NginxなどのWebサーバー層でレート制限をかける
アプリケーションコード(PHPやNode.jsなど)に処理が到達する前に、Webサーバーやリバースプロキシの段階で弾くのが最も効率的で安全です。
以下は、NginxでIPアドレス単位のレート制限を設定する際の実用的な設定例です。
# nginx.conf の設定例
http {
# 1分間に10リクエストまでを許可する領域(ゾーン)を定義
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/m;
server {
listen 80;
server_name example.com;
location /api/ {
# 定義したゾーンを適用。burst=5 で急なバースト(少しの許容量)を許可
limit_req zone=api_limit burst=5 nodelay;
# アプリケーションサーバーへ転送
proxy_pass http://backend_server;
# 【重要】プロキシ経由の正しいIPを引き継ぐための設定
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
}
- 解説:この設定をしておけば、攻撃者がどれだけヘッダーを偽装しようとも、Nginxが実際のTCP接続元IPを正しく評価し、10回を超えたリクエストを即座に
429 Too Many Requestsで突っ返すことができます。アプリケーションが攻撃の負荷でダウンするのを防げますね。
—
5. まとめ
いかがでしたでしょうか?
今回は、APIのレート制限の仕組みから、攻撃者がIPローテーションやヘッダー偽装を使ってそれをどうやってすり抜けるのか、そして私たちがどのようにシステムを守るべきかを見てきました。
セキュリティ対策は、一度やったら終わりではなく、イタチごっこの側面を持っています。しかし、「どこが信頼できて、どこが嘘をつかれやすいポイントなのか」を正しく理解していれば、脅威を最小限に抑える頑丈なシステムを築くことができます。
「家の鍵をしっかりかけ、怪しい訪問者はインターホン越しにしっかり確認する」――そんな身近な防犯意識を忘れずに、安全で快適なWebサービスを一緒に作っていきましょう!一歩ずつ、確実にスキルアップしていけば大丈夫です。応援しています!
コメント