Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

x-forwarded-prefix doesn't work properly #44572

Closed
ppodds opened this issue Dec 11, 2021 · 2 comments
Closed

x-forwarded-prefix doesn't work properly #44572

ppodds opened this issue Dec 11, 2021 · 2 comments

Comments

@ppodds
Copy link

ppodds commented Dec 11, 2021

Symfony version(s) affected

5.3

Description

I use Nginx as my reverse proxy of my laravel project and use the following setting.

server {
  listen 80;
  listen [::]:80;

  server_name _;
  location /to/project/ {
    proxy_set_header X-Forwarded-Prefix /to/project;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
    proxy_set_header X-Forwarded-Host my.domain;
    proxy_set_header X-Forwarded-Port 443;
    # laravel backend at localhost:8080
    proxy_pass http://localhost:8080/;
}

While x-forwarded-host, x-forwarded-port, and x-forwarded-proto work perfectly, x-forwarded-prefix doesn't. This problem might result from the method named getTrustedValues ( in /Symfony/Component/HttpFoundation/Request.php)

Below is the original code in Request.php, which should get the base url, and it calls getTrustedValues(self::HEADER_X_FORWARDED_PREFIX) to get the trusted prefix, but getTrustedValues could not deal with this type, so it always returns [](in my case).

/**
 * Returns the root URL from which this request is executed.
 *
 * The base URL never ends with a /.
 *
 * This is similar to getBasePath(), except that it also includes the
 * script filename (e.g. index.php) if one exists.
 *
 * @return string The raw URL (i.e. not urldecoded)
 */
public function getBaseUrl(): string
{
    $trustedPrefix = '';

    // the proxy prefix must be prepended to any prefix being needed at the webserver level
    if ($this->isFromTrustedProxy() && $trustedPrefixValues = $this->getTrustedValues(self::HEADER_X_FORWARDED_PREFIX)) {
        $trustedPrefix = rtrim($trustedPrefixValues[0], '/');
    }

    return $trustedPrefix.$this->getBaseUrlReal();
}

...

private function getTrustedValues(int $type, string $ip = null): array
...

How to reproduce

Create an Nginx reverse proxy and do the following setting.

server {
  listen 80;
  listen [::]:80;

  server_name _;
  location /to/project/ {
    proxy_set_header X-Forwarded-Prefix /to/project;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
    proxy_set_header X-Forwarded-Host my.domain;
    proxy_set_header X-Forwarded-Port 443;
    # laravel backend at localhost:8080
    proxy_pass http://localhost:8080/;
}

laravel (route/web.php)

use Illuminate\Http\Request;

Route::get('/test', function (Request $request) {
    // base url would be "/test" not "/to/project/test"
    dd($request->getBaseUrl());
    return response("test", 200);
});

Possible Solution

Create another method to get prefix from x-forwarded-prefix, or support HEADER_X_FORWARDED_PREFIX in getTrustedValues()

Additional Context

No response

@xabbuh
Copy link
Member

xabbuh commented Dec 12, 2021

Did you configure your application so that X-Forwared-Prefix is treated as a trusted header? Can you create an example application that allows to reproduce your issue?

@ppodds
Copy link
Author

ppodds commented Dec 12, 2021

I found the problem result from laravel framework.

laravel not support x-forwarded-prefix header in /src/Illuminate/Http/Middleware/TrustProxies.php

I can use the following code to solve the problem.

protected function getTrustedHeaderNames()
{
    switch ($this->headers) {
        case 'HEADER_X_FORWARDED_AWS_ELB':
        case Request::HEADER_X_FORWARDED_AWS_ELB:
            return Request::HEADER_X_FORWARDED_AWS_ELB;

        case 'HEADER_FORWARDED':
        case Request::HEADER_FORWARDED:
            return Request::HEADER_FORWARDED;

        case 'HEADER_X_FORWARDED_FOR':
        case Request::HEADER_X_FORWARDED_FOR:
            return Request::HEADER_X_FORWARDED_FOR;

        case 'HEADER_X_FORWARDED_HOST':
        case Request::HEADER_X_FORWARDED_HOST:
            return Request::HEADER_X_FORWARDED_HOST;

        case 'HEADER_X_FORWARDED_PORT':
        case Request::HEADER_X_FORWARDED_PORT:
            return Request::HEADER_X_FORWARDED_PORT;

        case 'HEADER_X_FORWARDED_PROTO':
        case Request::HEADER_X_FORWARDED_PROTO:
            return Request::HEADER_X_FORWARDED_PROTO;

        // add this to support x-forwarded-prefix
        case 'HEADER_X_FORWARDED_PREFIX':
        case Request::HEADER_X_FORWARDED_PREFIX:
            return Request::HEADER_X_FORWARDED_PREFIX;

        // add | Request::HEADER_X_FORWARDED_PREFIX
        default:
            return 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;
    }

    return $this->headers;
}

The code in symfony works properly. Thank you.

@ppodds ppodds closed this as completed Dec 12, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants