LaravelでIP制限をかけたいと思ったときにIPアドレスの取得が必要なのですが、思ったIPアドレスが取得できなかったので調べてみました。
前提
AWSで構築された環境でプライベートサブネットにLaravelのサーバーを設置した状態です。
Laravelのサーバーにリクエストを投げるにはELB(ロードバランサー)を経由してアクセスします。
環境
Laravel 7.*
IPアドレスの取得
Laravelではリクエスト元のIPアドレスを取得するのは簡単です。
Illuminate\Http\Request
のip
メソッドを使うことで取得できます。
public function hoge(Request $request)
{
$ip = $request->ip();
return $ip;
}
簡単ですね。
やりたかったこと
ミドルウェアでIP制限をかけたかったため、リクエスト元のIPアドレスが許可するIPアドレスに含まれるかを検証しようとしました。
public function handle(Request $request, Closure $next)
{
$allowedIps = [
'10.0.0.0',
'10.0.0.1',
];
$isAllow = IpUtils::checkIp($request->ip(), $allowedIps);
if ($isAllow) {
return $next($request);
}
}
LaravelでIP制限をかけるにはこちらの記事が参考になりました。
ハマったポイント
Laravelではロードバランサーやプロキシを経由の場合だと$request->ip()
で取得できるIPアドレスは実際のクライアントのIPではなくロードバランサーのIPを取得します。
クライアントIPアドレスが「10.0.0.0」であっても、ロードバランサーのIPが「11.0.0.0」だとすると$request->ip()
で取得できるIPは「11.0.0.0」になります。
解決方法
IP制限をかけるパターンはいくつか存在しますが、最終的に良いなと思ったのはAWS WAFでIP制限をかけるということです。
これまでLaravelでIP制限する方法を考えてきましたが、そもそももっとクライアントよりに位置する場所でIP制限を行ってあげるほうがセキュリティ的にも安全な気がします。
とはいってもLaravelで実際のクライアントIPを取得することはできないのかということになってしまうので、私が実際のクライアントIPを取得できた方法を紹介します。
※すべての方法を試したわけではないので他にもクライアントIPを取得する方法はあるかも知れません。
1つ目はX-Forword-For
ヘッダーを使って取得する方法です。
X-Forword-For – HTTP | MDN
X-Forwarded-For
(XFF) ヘッダーは、 HTTP プロキシ又はロードバランサーを通過してウェブサーバーへ接続したクライアントの、送信元 IP アドレスを特定するために事実上の標準となっているヘッダーです。
Laravelだと$request->header('X-Forword-For')
で取得することができます。
レスポンスとしては文字列で、「”10.0.0.0, 10.0.0.1, 10.0.0.2, …”」というようにIPアドレスごとにカンマとスペース区切られます。
最も左のIPアドレスが元のクライアントIPになり、右になるに連れてサーバー側のIPになります。
上記から、送信元IPを取得するには一番左のIPを取り出せば可能です。
少し強引ですが、下記のように取ってみました。
$xForwardedFor = $request->header('X-Forwarded-For');
$ips = explode(',', $xForwardedFor);
$clientIp = $ips[0];
2つ目はSymfony\Component\HttpFoundation\Request
のgetClientIps
メソッドを使用することでも可能です。
/**
* Returns the client IP addresses.
*
* In the returned array the most trusted IP address is first, and the
* least trusted one last. The "real" client IP address is the last one,
* but this is also the least trusted one. Trusted proxies are stripped.
*
* Use this method carefully; you should use getClientIp() instead.
*
* @return array The client IP addresses
*
* @see getClientIp()
*/
public function getClientIps()
{
$ip = $this->server->get('REMOTE_ADDR');
if (!$this->isFromTrustedProxy()) {
return [$ip];
}
return $this->getTrustedValues(self::HEADER_X_FORWARDED_FOR, $ip) ?: [$ip];
}
このメソッドは信頼できるプロキシから発信されたIPを取得します。
返り値が配列ですが、X-Forwarded-Forとは逆で最も信頼できるIPアドレスが最初になり、最も信頼できないIPアドレスが最後になります。
送信元のIPを取得するには最後のIPを取得することで可能です。
この辺はTrustProxies
周りの調査も必要がありそうです。
いろいろ調べていて思ったのはLaravelのメソッドの処理を追っていくのは結構勉強になりますね。
コメント