Jul 21, 2026·6 min read·31 visits
Guzzle HTTP client leaks client-side URI fragments containing sensitive data (like access tokens) in Referer headers when auto-redirects are configured with referer tracking enabled.
An issue in Guzzle's RedirectMiddleware allows client-side URI fragments to be leaked over the network in Referer headers during same-scheme HTTP redirects.
The vulnerability resides within the Guzzle HTTP client's redirect handling component, specifically GuzzleHttp\Psr7 and the GuzzleHttp\Middleware\RedirectMiddleware class. Guzzle is a highly popular PHP HTTP client widely used in modern web applications to coordinate integrations and third-party API interactions. Under certain circumstances, configurations that automate redirect processing can expose sensitive client-side data over the network.\n\nWhen a developer explicitly configures the Guzzle client to automatically generate and forward the Referer header during HTTP redirects, the underlying middleware attempts to reconstruct the request's origin context. However, versions of the Guzzle library prior to 7.15.1 fail to validate the structure of the origin URI, enabling client-side fragment data to be serialized and processed over the network. This behavior directly expands the attack surface to include third-party services that receive these redirected request headers.\n\nThe vulnerability class corresponds to CWE-201 (Insertion of Sensitive Information Into Sent Data), stemming from an incorrect abstraction of URI boundaries. Standard web specifications indicate that URI fragments must never traverse the network. By violating this protocol, Guzzle transmits client-side processing states, such as OAuth tokens or single-sign-on hashes, directly to unauthorized network end-points during same-scheme redirections.
To understand the technical root cause, we must evaluate the distinction between a URI's query parameters and its fragment components. According to RFC 3986 Section 3.5, the fragment identifier contains client-side instructions that identify a secondary resource. Traditional user agents, such as modern web browsers, separate the fragment component from the target URI before executing any HTTP request over the network. Guzzle, as a programmatic HTTP client, implements PSR-7 standards where the entire URI is held in memory as an instance of UriInterface.\n\nWhen Guzzle's RedirectMiddleware intercepts an HTTP 3xx redirect response, it checks if same-scheme redirection is occurring (for instance, redirecting from one HTTPS endpoint to another HTTPS endpoint). If the 'allow_redirects' configuration has 'referer' tracking enabled, Guzzle programmatically builds a new Referer header. It extracts the original request's URI object and attempts to clean it before adding it to the headers array.\n\nThe structural failure in Guzzle's sanitization process is that it only strips the user credentials (username and password) via the withUserInfo('') method. The middleware does not attempt to purge the fragment component. Consequently, the original URI is cast directly to a string, causing Guzzle to serialize the client-side fragment and send it to the destination server in the Referer header.
Analyzing the source code of Guzzle's RedirectMiddleware.php reveals the precise implementation bug. In vulnerable versions, the middleware validates that same-scheme redirects are occurring prior to constructing the Referer header, but it lacks any logical checks to purge the fragment data.\n\nBelow is the code comparison between the vulnerable and patched implementations of the modifyRequest function within Guzzle's RedirectMiddleware class:\n\nphp\\n// Vulnerable Implementation (Guzzle < 7.15.1)\\nif ($options['allow_redirects']['referer']\\n && $modify['uri']->getScheme() === $request->getUri()->getScheme()\\n) {\\n // Only user info is stripped; the URI fragment is retained\\n $uri = $request->getUri()->withUserInfo('');\\n $modify['set_headers']['Referer'] = (string) $uri;\\n} else {\\n $modify['remove_headers'][] = 'Referer';\\n}\\n\\n// Patched Implementation (Guzzle >= 7.15.1)\\nif ($options['allow_redirects']['referer']\\n && $modify['uri']->getScheme() === $request->getUri()->getScheme()\\n) {\\n // Both user info and fragment are explicitly stripped\\n $uri = $request->getUri()->withUserInfo('')->withFragment('');\\n $modify['set_headers']['Referer'] = (string) $uri;\\n} else {\\n $modify['remove_headers'][] = 'Referer';\\n}\\n\n\nThe patch resolves this by appending the withFragment('') call to the method chain. This returns a cloned UriInterface instance with the fragment property set to an empty string. However, security researchers should observe that query parameters are not stripped. If Guzzle initiates a redirect to a cross-origin server, any sensitive data held in query parameters (such as ?api_key=SECRET) will still be transmitted because Guzzle only validates that the redirect target's scheme matches the source scheme, leaving further domain validation to the developer.
An attacker seeking to exploit this vulnerability must target an application that communicates with a service that handles secrets inside URI fragments, such as an OAuth 2.0 authorization server using the implicit grant flow. The client must also have Guzzle's referer-tracking redirect behavior explicitly configured. The attacker must control the destination of a redirect, either via an open redirect flaw on the trusted host or by directly manipulating the client to follow a link to an attacker-controlled server.\n\nWhen the Guzzle client issues a request containing a fragment and the trusted endpoint responds with a 302 Redirect to the attacker's server, Guzzle automatically generates the Referer header. It retains the fragment in the Referer header, sending it to the attacker's host. The attacker then parses their server's access logs to extract the secret.\n\nmermaid\\ngraph LR\\n Client[\"Guzzle Client\\n'allow_redirects.referer' => true\"] -->|\"1. GET /callback#token=SECRET\"| Trusted[\"Trusted Host\\n(trusted-api.com)\"]\\n Trusted -->|\"2. 302 Redirect\\nLocation: https://evil.com/logger\"| Client\\n Client -->|\"3. GET /logger\\nReferer: https://trusted-api.com/callback#token=SECRET\"| Attacker[\"Attacker Server\\n(evil.com)\"]\\n\n\nNo weaponized exploit tools are currently documented in the wild. However, test assertions in Guzzle's test suite confirm that the fragment is transmitted when this code path is traversed under vulnerable configurations.
The impact of this vulnerability is a high-risk disclosure of confidential secrets. If an application utilizes fragments to pass temporary authorization keys, single-sign-on parameters, or OAuth state tokens, these values are immediately exposed to any external host that receives a redirect from the client.\n\nAn attacker obtaining these tokens can perform session hijacking, impersonate the application user, or access private application-programming interfaces. The vulnerability has a CVSS v3.1 base score of 5.9, reflecting a high confidentiality impact, with low complexity and no privileges required, balanced by the configuration prerequisites.\n\nBecause this vulnerability has no assigned CVE ID, it is not listed in the CISA KEV catalog, and EPSS scores are not calculated. Nevertheless, the lack of a formal CVE does not diminish the potential severity for systems using insecure redirect configurations.
The primary remediation path is upgrading the guzzlehttp/guzzle package to version 7.15.1 or later. The security release replaces the vulnerable code path and guarantees that all URI fragments are stripped prior to Referer header serialization.\n\nIf upgrading the library is not immediately possible, you should verify that Guzzle's referer tracking is disabled. This is Guzzle's default behavior. Ensure that any client instantiations do not explicitly enable this parameter, or explicitly configure it to false as shown below:\n\nphp\\n$client->request('GET', $uri, [\\n 'allow_redirects' => [\\n 'referer' => false, // Explicitly disabled\\n ],\\n]);\\n\n\nDevelopers can also manually sanitize incoming URIs by utilizing PSR-7 methods to strip fragments before executing requests. For example, calling $cleanUri = $uri->withFragment(''); guarantees that the client never processes or forwards client-side data over the network.
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
guzzlehttp/guzzle Guzzle | < 7.15.1 | 7.15.1 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-201 |
| Attack Vector | Network (AV:N) |
| CVSS Score | 5.9 (Medium) |
| Exploit Status | None/Unproven |
| EPSS Score | N/A |
| CISA KEV Status | Not Listed |
| Impact | Information Disclosure |
The product inserts sensitive information into a record or data flow that is sent to another actor.
A path traversal vulnerability (CWE-22) in Contao CMS allows unauthenticated remote attackers to bypass directory boundary restrictions in ImagesController and access files within the project directory.
Contao Open Source CMS versions 4.0.0 through 5.3.49 and 5.4.0-RC1 through 5.7.11 contain a Cross-Site Request Forgery (CSRF) vulnerability in backend parameter handling. The `RequestTokenListener` component validates anti-CSRF tokens solely for HTTP POST requests, while GET-based declarative guards run only when an `act` parameter is present in the query string. Consequently, custom backend actions dispatched via alternative parameters such as `key=` can execute without CSRF token verification when triggered by an authenticated user.
Contao CMS versions 4.1.0 through 5.3.49 and 5.4.0-RC1 through 5.7.11 fail to validate form submission tokens and enforce rate limiting when processing activation email resend requests via HTTP POST, enabling resource exhaustion and account state enumeration.
An information disclosure vulnerability in Contao CMS allows unauthenticated site visitors to view protected page titles, URLs, and text excerpts through search queries when protected page indexing is disabled after previously being enabled.
In Vikunja prior to version 2.6.0, relation creation via the CalDAV endpoint fails to invoke the TaskRelation.CanCreate authorization check. This missing access control allows an authenticated user to establish unauthorized relationships and perform write operations against any task, provided its unique identifier (UID) is known.
A cross-project information disclosure vulnerability in Vikunja allows authenticated users with read access to one project to view private task details from unauthorized projects via subtask expansion parameters.