Affected versions
Symfony Routing 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 Routing 5.4.56, 6.4.50, 7.4.23, 8.1.11.
Description
For routes that have a host, CompiledUrlMatcher matches the string $host.'.'.$pathinfo against the host regexp followed by the path regexp. A host placeholder could match /, so it could eat the start of the path, while UrlMatcher matches the host and the path separately and finds no route. Rules that look at the path, such as access_control entries and the pattern of firewalls, judge the real path, so they do not apply, while the controller of the route runs.
With the route /admin/report/{id}, the host app.{domain} and access_control: [{ path: ^/admin, roles: ROLE_ADMIN }], an anonymous request for /admin/report/1 is sent to the login page, while one with the host app and the path /x./admin/report/1 reaches the controller, with domain="/x". With the requirement domain: .+, the real host name works: app.example.com and /x./admin/report/1 match with domain="example.com./x".
Applications are affected when they have routes with host placeholders protected by path-based rules. With the default requirement, the Host must be the host of the route without its last labels, such as app: framework.trusted_hosts blocks that, while a web server that only serves known names may not, for instance nginx passing the raw Host header to FastCGI for a request line that carries an absolute URI. With a requirement that accepts /, such as .+, the real host name works and trusted_hosts does not help.
Resolution
The compiled regexp now captures the path ahead of each host alternative that has placeholders, and checks after the host regexp that the rest of the subject is that path. Since a host cannot contain /, the path starts at the first /, and host placeholders can no longer match any part of it, whatever their requirements. Valid requests keep the same route and the same parameters, as verified against UrlMatcher over eleven million generated cases.
The fix is in the dumper, so an application keeps its vulnerable compiled routes until its cache is cleared. Clear the cache after upgrading.
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 Vincent Cassiau (CyberVanguard) for reporting the issue and Nicolas Grekas for providing the fix.
We would also like to thank Bruno Dias and Ana Lisboa from Checkmarx for their help getting a CVE assigned.