laravel / framework

The Laravel Framework.
https://laravel.com
MIT License
32.21k stars 10.9k forks source link

route() host overwritten by X-Forwarded-Host #29800

Closed event15 closed 5 years ago

event15 commented 5 years ago

Description:

If the user sends a query with the manually set X-Forwarded-Host header, then the route() function will return an array with the key "host" with the changed address. This may be a security problem - when I use route('my/page/url') in setAction() in any Form Builder (symfony form, laravel FormFactory etc.), then action attribute can be overridden by the attacker's domain.

Steps To Reproduce:

  1. Get postman or other soft to modify headers.
  2. Set header X-Forwarded-Host with domain, ex. www.google.com
  3. Refresh page with form, when the page has setAction() method with route(), for example:
    $form = FormFactory::createBuilder()
            ->setMethod('POST')
            ->setAction(route('homepage/signup'))

The action url is is overwritten. All data is correct except host.

Good to read

https://www.acunetix.com/blog/articles/automated-detection-of-host-header-attacks/

bonzai commented 5 years ago

You can set trusted hosts to prevent header injection attacks:

Request::setTrustedHosts([
    '^(.+\.)?example\.com$',
    '^(.+\.)?example\.org$',
]);
adrianorsouza commented 5 years ago

Another way to mitigate this, if you serve your Laravel application with Nginx is to listen only to those valid domains and send an empty response for invalid ones:

Unknown hosts:

server {
    listen 80;
    server_name _;
    return 444;
}

All valid hosts:

server {
  listen 80;
  server_name .example.net .example.com;
  ...
}

Also if you define your routes within a domain group you can also be safe:

Route::domain('example.com')->group(function () {
 ...
});
event15 commented 5 years ago

Thanks!

PirunSeng commented 3 years ago

You can set trusted hosts to prevent header injection attacks:

Request::setTrustedHosts([
  '^(.+\.)?example\.com$',
  '^(.+\.)?example\.org$',
]);

Excuse me @bonzai could you point where to put this? Thanks.

DarkcoderSe commented 2 years ago

You also can filter each request in the AppServiceProvider boot method for the valid host.

    $appUrl = env('APP_URL');
    $appUrls = explode("//", $appUrl ?? '');

    $allowedHosts = [$appUrl, $appUrls[1]];
    $currentHost = request()->getHttpHost();

    if (!in_array($currentHost, $allowedHosts))
    {
        abort(404);
    }