クライアントのIPアドレス取得でハマった話【Laravel】

get ip addressLaravel

LaravelでIP制限をかけたいと思ったときにIPアドレスの取得が必要なのですが、思ったIPアドレスが取得できなかったので調べてみました。

前提

AWSで構築された環境でプライベートサブネットにLaravelのサーバーを設置した状態です。

Laravelのサーバーにリクエストを投げるにはELB(ロードバランサー)を経由してアクセスします。

環境

Laravel 7.*

IPアドレスの取得

Laravelではリクエスト元のIPアドレスを取得するのは簡単です。

Illuminate\Http\Requestipメソッドを使うことで取得できます。

    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 ミドルウェアで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-Forwarded-For (XFF) ヘッダーは、 HTTP プロキシ又はロードバランサーを通過してウェブサーバーへ接続したクライアントの、送信元 IP アドレスを特定するために事実上の標準となっているヘッダーです。

X-Forword-For – HTTP | MDN

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\RequestgetClientIpsメソッドを使用することでも可能です。

    /**
     * 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のメソッドの処理を追っていくのは結構勉強になりますね。

コメント

タイトルとURLをコピーしました