Skip to content

TrustProxies with at: * does not work correctly when request has passed through multiple proxies #60725

Description

@mattford

Laravel Version

12.63.0, 13.x

PHP Version

8.4

Database Driver & Version

No response

Description

I have an application running on ELB behind a CloudFront distribution. Both ELB and CloudFront forward the request downstream resulting in my X-Forwarded-For header looking like this:

1.1.1.1, 2.2.2.2

Where 1.1.1.1 is my actual client IP address, 2.2.2.2 is CloudFront, and 3.3.3.3 is ELB.

I have trusted all proxies with the line:

$middleware->trustProxies(
            at: '*',
            headers: Request::HEADER_X_FORWARDED_FOR |
            Request::HEADER_X_FORWARDED_HOST |
            Request::HEADER_X_FORWARDED_PORT |
            Request::HEADER_X_FORWARDED_PROTO |
            Request::HEADER_X_FORWARDED_AWS_ELB
        );

However when I call $request->ip() I get 2.2.2.2

I've dug into this a bit and discovered this is because when Laravel trusts all proxies, it sets the list of trusted IPs to the REMOTE_ADDR (3.3.3.3 in my case).
This list ends up getting passed to Symfony, which then returns the latest untrusted IP in the list, which is 2.2.2.2.

As Symfony does not support wildcards, my proposed fix for this is to gather a list of all X-FORWARDED-FOR and FORWARDED IP addresses, and set them into the $trustedProxies array, so that Symfony trusts the entire chain and will therefore return the first trusted IP, 1.1.1.1 in my case.

I note that there is a test case for this but it appears to be incorrect, according to https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-For the leftmost IP is the original client IP, which reflects in what I am seeing.

I have a fix in progress for this, will raise a PR

Steps To Reproduce

  1. Start a Laravel app with TrustProxies:*
  2. Craft a request with multiple IP addresses in X-FORWARDED-FOR or FORWARDED header
  3. Observe that the last IP in the list is used as the original client IP, not the first!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions