現場で泣かないための「X-Forwarded-For」完全攻略:プロキシ越しのクライアントIP特定術
Webインフラの現場にいると、必ず一度は直面する壁がある。「ログを見ると全部ロードバランサー(LB)のIPアドレスになっていて、誰がアクセスしているか分からない」という問題だ。
HTTPはステートレスなプロトコルであり、プロキシやLBを挟むと、オリジンサーバーからは「最後に接続してきた相手」しか見えない。そこで登場するのが、標準仕様ではないものの、事実上の世界標準となっているヘッダー『X-Forwarded-For』(以下、XFF)だ。
今回は、このXFFの仕組みを解剖し、セキュリティ上の落とし穴と、現場で迷わないための実装術を解説する。
—
1. なぜ「XFF」が必要なのか?
HTTP/1.1の世界では、通信経路にプロキシサーバーを挟むことが前提となっている。クライアントとサーバーの間にLB、CDN、WAFといった「中継者」が存在する場合、オリジンサーバーが直接受け取るTCPコネクションの送信元IPは、必ず直前の「中継者」のものになる。
しかし、ログ分析、IPベースのレートリミット、地理的な最適化のためには、どうしても「最初の送信元(クライアント)」のIPアドレスが必要だ。これを解決するために、中継したサーバーが「元々の送信元IPはこのIPだよ」とバトンを渡す仕組みがXFFである。
通信シーケンスのイメージ
[Client: 1.1.1.1]
↓ (HTTP Request: X-Forwarded-Forなし)
[Load Balancer: 10.0.0.1]
↓ (HTTP Request: X-Forwarded-For: 1.1.1.1)
[Origin Server]
—
2. XFFの書式と解釈
XFFは単純な文字列の羅列だ。新しい中継者が現れるたびに、既存のヘッダー値の末尾にカンマ区切りでIPアドレスを追加していく。
- ヘッダーの構造: `X-Forwarded-For: <クライアントIP>, <プロキシ1>, <プロキシ2>`
- 読み方: 一番左が「本当のクライアントIP」、右へ行くほど「直近のプロキシ」となる。
現場の注意点:IPの偽装
ここがエンジニアの腕の見せ所だ。XFFは「善意のプロキシ」が書き込むものだが、悪意のあるクライアントが最初からヘッダーを付与してリクエストを送ってきたらどうなるか?
オリジンサーバー側で単純に「XFFの先頭を信頼する」と実装すると、攻撃者にIPを偽装されてしまう。実務では、「信頼できるプロキシ(LBやCDN)のみが書き換えたヘッダーを信頼する」という設計が鉄則だ。
—
3. 実装のTips:設定とコード例
Nginxでの設定例
NginxをLBとして使う場合、`X-Forwarded-For`を適切に付与する設定は必須だ。
server {
listen 80;
location / {
# $proxy_add_x_forwarded_for:
# 既存のXFFがあればカンマ区切りで追加、なければクライアントIPを付与する便利な変数
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# 接続先サーバーへ転送
proxy_pass http://backend_servers;
}
}
Python (Flask) でクライアントIPを取得する
バックエンドでIPを取得する際、そのまま `request.remote_addr` を使うとLBのIPしか取れない。以下のようにヘッダーを考慮する必要がある。
from flask import request
@app.route(‘/’)
def get_client_ip():
# XFFヘッダーが存在すれば、その先頭(一番左)を採用する
# ※ただし、この実装には「信頼できる中継元から来たか」の検証が別途必要!
xff = request.headers.get(‘X-Forwarded-For’)
if xff:
client_ip = xff.split(‘,’)[0].strip()
else:
client_ip = request.remote_addr
return f”あなたのIPは {client_ip} です”
—
4. デバッグの現場:curlで確認する
何かおかしいと思ったら、まずは手元でヘッダーを強制注入して、サーバーの挙動を確認しよう。
curlでヘッダーを偽装してリクエストを送るテスト
curl -H “X-Forwarded-For: 192.0.2.1” http://your-api-endpoint.com/debug
このコマンドを打って、サーバー側のログに `192.0.2.1` が出力されるなら、そのサーバーは「外部からのヘッダーを無条件に信頼している」可能性がある。セキュリティ上の脆弱性になり得るため、必ずチェックしてほしい。
—
5. シニアエンジニアからの教訓
最後に、実務で必ず守るべきルールを3つ伝授する。
1. 信頼の境界線を引く: 信頼できない外部ネットワークから来るリクエストに対して、XFFをそのまま信じてはいけない。必ず「信頼できるIPアドレス範囲(LBのセグメント等)」からのリクエストかを確認すること。
2. RFC 7239 (Forwardedヘッダー) の検討: XFFは非公式規格だ。モダンな環境では、より構造的で標準化された `Forwarded` ヘッダーの利用も検討しよう。ただし、まだ対応していないレガシーな装置も多いため、現状はXFFと共存させるのが現実解だ。
3. ログには全てを残す: トラブルシューティングの際、XFFの値が書き換わっているのか、そもそも来ていないのかを切り分けるために、オリジンサーバーのアクセスログには必ず `$http_x_forwarded_for` を出力させておくこと。
ネットワークは「見えないもの」をいかに「可視化」するかの勝負だ。XFFという小さなバトンを正しく繋ぎ、確実なログを残すことこそが、障害発生時に深夜のオフィスで泣かずに済むための唯一の道である。
コメント