Affected versions

Symfony HTTP Foundation versions <5.4.56, >=6, <6.4.50, >=7, <7.4.23, >=8, <8.1.11 are affected by this security issue.

The issue has been fixed in Symfony HTTP Foundation 5.4.56, 6.4.50, 7.4.23, 8.1.11.

Description

When Request::getBaseUrl() does not find the script name at the start of the request URI, it looks for it further into the path. This supports web servers that rewrite every URL to a front controller in a subdirectory. The base URL then kept everything before the script name, and the client chooses that part. A request URI starting with // or /\ made the base URL start with a host.

With SCRIPT_NAME=/public/index.php, a request for //evil.example/public/index.php/login is routed to /login, with the base URL //evil.example/public/index.php and the base path //evil.example/public. The URLs generated for that page then point to evil.example:

  • links built with path() and the actions of forms, so a login form sends the credentials of the user to evil.example;
  • redirects to generated URLs, such as the ones of redirectToRoute();
  • assets referenced with a relative path through asset(), so the page can load scripts from evil.example.

The attacker has to make a user follow a crafted link, such as https://app.example//evil.example/public/index.php/login. framework.trusted_hosts does not help, since the host of the request is the real one.

Applications are affected when their front controller is in a subdirectory and the web server sends it URIs that do not start with that directory. A .htaccess file at the root of the project that rewrites every URL to public/ does that, for instance.

Applications are also affected when the web server sets SCRIPT_NAME to the path of the request. Some nginx configurations do that when they send every request to the front controller without fastcgi_split_path_info. php -S does it too, with a router script outside the document root. A request for /\evil.example/index.php then gets the base URL /\evil.example/index.php, which browsers read as a link to evil.example.

The configurations recommended in the Symfony documentation are not affected.

Resolution

Request::getBaseUrl() now returns an empty base URL when the request URI starts with // or /\.

The patches for this issue are available here for branch 5.4 (and forward-ported to 6.4, 7.4, 8.1 and 8.2).

Credits

We would like to thank Nicolas Grekas for reporting the issue and providing the fix.