phplaravellaravel-9hostheaders

How to prevent "Host Header Attack" in Laravel?


I am willing to prevent host header attack in my laravel 9 app. I am using this command in terminal to test for that attack - curl http://127.0.0.1:8000 -H "Host: evil.com".

After searching on internet i have enabled this \App\Http\Middleware\TrustHosts::class middleware in kernel.php file.

Even i have created my own middleware to check for host header like this

<?php

namespace App\Http\Middleware;

use Closure;

class HostHeaderValidationMiddleware
{
    public function handle($request, Closure $next)
    {
        $expectedDomain = '127.0.0.1';
        // echo $_SERVER['HTTP_HOST'];
        // echo "<br>";
        // echo $_SERVER['SERVER_NAME'];exit;
        // Validate and sanitize the host header
        if ($request->getHost() !== $expectedDomain || $_SERVER['SERVER_NAME'] !== $expectedDomain) {
            abort(400, 'Invalid request.');
        }

        // Validating referer header
        $referer = $request->headers->get('Referer');
        if ($referer && parse_url($referer, PHP_URL_HOST) !== $expectedDomain) {
            abort(400, 'Invalid Referer header.');
        }

        return $next($request);
    }
}

But after running this command it is not even going into this middleware it is giving this output in terminal

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
        <meta http-equiv="refresh" content="0;url='http://evil.com/login'" />

        <title>Redirecting to http://evil.com/login</title>
    </head>
    <body>
        Redirecting to <a href="http://evil.com/login">http://evil.com/login</a>.
    </body>
</html>

Is ther any other way to prevent host header attack in Laravel or PHP


Solution

  • You can skip your own middleware and use the laravel trusted hosts middleware (The one shipped with laravel which you enabled).

    Make sure that in the middleware you have defined your hosts, something like this:

    public function hosts(): array
    {
        return [
            'localhost', '127.0.0.1',
            $this->allSubdomainsOfApplicationUrl(),
        ];
    }
    

    Alternativley you can skip adding localhost and use the defaults like this:

    public function hosts(): array
    {
        return [
            $this->allSubdomainsOfApplicationUrl(),
        ];
    }
    

    In that case you need to make sure that APP_URL in your .env file is correct. Then only requests to the APP_URL host (and it's subdomains) are allowed.

    Note: That your port 8000 suggests you are running laravel with php artisan serve. If your app is local laravel will skip the trusted hosts checks. (If you look into the Illuminate\Http\Middleware\TrustHosts class you will see this check:

    /**
     * Determine if the application should specify trusted hosts.
     *
     * @return bool
     */
    protected function shouldSpecifyTrustedHosts()
    {
        return ! $this->app->environment('local') &&
               ! $this->app->runningUnitTests();
    }
    

    So make sure in your .env file APP_ENVis NOT set to local. Set it to something like production instead:

    APP_ENV=production
    

    When you have followed my instructions above curl http://127.0.0.1:8000 -H "Host: evil.com" should return a 404 (Not Found) status code and page