VYPR
High severityNVD Advisory· Published May 25, 2022· Updated Apr 23, 2025

Cross-domain cookie leakage in Guzzle

CVE-2022-29248

Description

Guzzle is a PHP HTTP client. Guzzle prior to versions 6.5.6 and 7.4.3 contains a vulnerability with the cookie middleware. The vulnerability is that it is not checked if the cookie domain equals the domain of the server which sets the cookie via the Set-Cookie header, allowing a malicious server to set cookies for unrelated domains. The cookie middleware is disabled by default, so most library consumers will not be affected by this issue. Only those who manually add the cookie middleware to the handler stack or construct the client with ['cookies' => true] are affected. Moreover, those who do not use the same Guzzle client to call multiple domains and have disabled redirect forwarding are not affected by this vulnerability. Guzzle versions 6.5.6 and 7.4.3 contain a patch for this issue. As a workaround, turn off the cookie middleware.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Guzzle PHP HTTP client prior to 6.5.6 and 7.4.3 fails to validate cookie domain against server domain, allowing malicious servers to set cookies for unrelated domains when cookie middleware is enabled.

Vulnerability

Guzzle, a PHP HTTP client, versions prior to 6.5.6 and 7.4.3, contains a vulnerability in the cookie middleware. The middleware does not verify that the domain in a Set-Cookie header matches the domain of the server that issued it, enabling a malicious server to set cookies for arbitrary domains [1][2]. The cookie middleware is disabled by default; only users who explicitly enable it via ['cookies' => true] or manually add it to the handler stack are affected [1].

Exploitation

An attacker must control a server that the Guzzle client contacts. If the client has the cookie middleware enabled and makes requests to multiple domains (or follows redirects), the attacker can inject a Set-Cookie header with a domain attribute pointing to a different domain (e.g., a victim's site). The client will then store that cookie and send it in subsequent requests to the victim domain [1][2]. No authentication or user interaction beyond the initial request is required.

Impact

Successful exploitation allows an attacker to set arbitrary cookies on unrelated domains from the perspective of the Guzzle client. This can lead to session fixation, cross-site request forgery (CSRF) token leakage, or other attacks that rely on cookie manipulation, depending on how the client uses cookies [1]. The attacker gains the ability to influence cookie state for domains they do not control.

Mitigation

The vulnerability is patched in Guzzle versions 6.5.6 and 7.4.3 [1][2]. Users should upgrade to these versions or later. As a workaround, disable the cookie middleware entirely if not needed [1]. The Guzzle 6.x branch reached end-of-life on 2023-10-31, so users on 6.x should migrate to 7.x [3]. This CVE is not listed in CISA's Known Exploited Vulnerabilities catalog as of the publication date.

AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
guzzlehttp/guzzlePackagist
< 6.5.66.5.6
guzzlehttp/guzzlePackagist
>= 7.0.0, < 7.4.37.4.3

Affected products

3

Patches

1
74a8602c6fae

[7.x] Fix cross-domain cookie leakage (#3018)

https://github.com/guzzle/guzzleGraham CampbellMay 25, 2022via ghsa
5 files changed · +55 15
  • CHANGELOG.md+4 0 modified
    @@ -2,6 +2,10 @@
     
     Please refer to [UPGRADING](UPGRADING.md) guide for upgrading to a major version.
     
    +## 7.4.3 - 2022-05-25
    +
    +* Fix cross-domain cookie leakage
    +
     ## 7.4.2 - 2022-03-20
     
     ### Fixed
    
  • README.md+7 7 modified
    @@ -60,13 +60,13 @@ composer require guzzlehttp/guzzle
     
     ## Version Guidance
     
    -| Version | Status     | Packagist           | Namespace    | Repo                | Docs                | PSR-7 | PHP Version |
    -|---------|------------|---------------------|--------------|---------------------|---------------------|-------|-------------|
    -| 3.x     | EOL        | `guzzle/guzzle`     | `Guzzle`     | [v3][guzzle-3-repo] | [v3][guzzle-3-docs] | No    | >= 5.3.3    |
    -| 4.x     | EOL        | `guzzlehttp/guzzle` | `GuzzleHttp` | [v4][guzzle-4-repo] | N/A                 | No    | >= 5.4      |
    -| 5.x     | EOL        | `guzzlehttp/guzzle` | `GuzzleHttp` | [v5][guzzle-5-repo] | [v5][guzzle-5-docs] | No    | >= 5.4      |
    -| 6.x     | Security fixes | `guzzlehttp/guzzle` | `GuzzleHttp` | [v6][guzzle-6-repo] | [v6][guzzle-6-docs] | Yes   | >= 5.5      |
    -| 7.x     | Latest     | `guzzlehttp/guzzle` | `GuzzleHttp` | [v7][guzzle-7-repo] | [v7][guzzle-7-docs] | Yes   | >= 7.2      |
    +| Version | Status         | Packagist           | Namespace    | Repo                | Docs                | PSR-7 | PHP Version  |
    +|---------|----------------|---------------------|--------------|---------------------|---------------------|-------|--------------|
    +| 3.x     | EOL            | `guzzle/guzzle`     | `Guzzle`     | [v3][guzzle-3-repo] | [v3][guzzle-3-docs] | No    | >=5.3.3,<7.0 |
    +| 4.x     | EOL            | `guzzlehttp/guzzle` | `GuzzleHttp` | [v4][guzzle-4-repo] | N/A                 | No    | >=5.4,<7.0   |
    +| 5.x     | EOL            | `guzzlehttp/guzzle` | `GuzzleHttp` | [v5][guzzle-5-repo] | [v5][guzzle-5-docs] | No    | >=5.4,<7.4   |
    +| 6.x     | Security fixes | `guzzlehttp/guzzle` | `GuzzleHttp` | [v6][guzzle-6-repo] | [v6][guzzle-6-docs] | Yes   | >=5.5,<8.0   |
    +| 7.x     | Latest         | `guzzlehttp/guzzle` | `GuzzleHttp` | [v7][guzzle-7-repo] | [v7][guzzle-7-docs] | Yes   | >=7.2.5,<8.2 |
     
     [guzzle-3-repo]: https://github.com/guzzle/guzzle3
     [guzzle-4-repo]: https://github.com/guzzle/guzzle/tree/4.x
    
  • src/Cookie/CookieJar.php+5 0 modified
    @@ -241,6 +241,11 @@ public function extractCookies(RequestInterface $request, ResponseInterface $res
                     if (0 !== \strpos($sc->getPath(), '/')) {
                         $sc->setPath($this->getCookiePathFromRequest($request));
                     }
    +                if (!$sc->matchesDomain($request->getUri()->getHost())) {
    +                    continue;
    +                }
    +                // Note: At this point `$sc->getDomain()` being a public suffix should
    +                // be rejected, but we don't want to pull in the full PSL dependency.
                     $this->setCookie($sc);
                 }
             }
    
  • src/Cookie/SetCookie.php+4 2 modified
    @@ -379,10 +379,12 @@ public function matchesDomain(string $domain): bool
     
             // Remove the leading '.' as per spec in RFC 6265.
             // https://tools.ietf.org/html/rfc6265#section-5.2.3
    -        $cookieDomain = \ltrim($cookieDomain, '.');
    +        $cookieDomain = \ltrim(\strtolower($cookieDomain), '.');
    +
    +        $domain = \strtolower($domain);
     
             // Domain not set or exact match.
    -        if (!$cookieDomain || !\strcasecmp($domain, $cookieDomain)) {
    +        if ('' === $cookieDomain || $domain === $cookieDomain) {
                 return true;
             }
     
    
  • tests/Cookie/CookieJarTest.php+35 6 modified
    @@ -271,7 +271,7 @@ public function getMatchingCookiesDataProvider()
         /**
          * @dataProvider getMatchingCookiesDataProvider
          */
    -    public function testReturnsCookiesMatchingRequests($url, $cookies)
    +    public function testReturnsCookiesMatchingRequests(string $url, string $cookies)
         {
             $bag = [
                 new SetCookie([
    @@ -386,16 +386,13 @@ public function getCookiePathsDataProvider()
                 ['/foo', '/'],
                 ['/foo/bar', '/foo'],
                 ['/foo/bar/', '/foo/bar'],
    -            ['foo', '/'],
    -            ['foo/bar', '/'],
    -            ['foo/bar/', '/'],
             ];
         }
     
         /**
          * @dataProvider getCookiePathsDataProvider
          */
    -    public function testCookiePathWithEmptySetCookiePath($uriPath, $cookiePath)
    +    public function testCookiePathWithEmptySetCookiePath(string $uriPath, string $cookiePath)
         {
             $response = (new Response(200))
                 ->withAddedHeader(
    @@ -407,13 +404,45 @@ public function testCookiePathWithEmptySetCookiePath($uriPath, $cookiePath)
                     "bar=foo; expires={$this->futureExpirationDate()}; domain=www.example.com; path=foobar;"
                 )
             ;
    -        $request = (new Request('GET', $uriPath))->withHeader('Host', 'www.example.com');
    +        $request = (new Request('GET', "https://www.example.com{$uriPath}"));
             $this->jar->extractCookies($request, $response);
     
             self::assertSame($cookiePath, $this->jar->toArray()[0]['Path']);
             self::assertSame($cookiePath, $this->jar->toArray()[1]['Path']);
         }
     
    +    public function getDomainMatchesProvider()
    +    {
    +        return [
    +            ['www.example.com', 'www.example.com', true],
    +            ['www.example.com', 'www.EXAMPLE.com', true],
    +            ['www.example.com', 'www.example.net', false],
    +            ['www.example.com', 'ftp.example.com', false],
    +            ['www.example.com', 'example.com', true],
    +            ['www.example.com', 'EXAMPLE.com', true],
    +            ['fra.de.example.com', 'EXAMPLE.com', true],
    +            ['www.EXAMPLE.com', 'www.example.com', true],
    +            ['www.EXAMPLE.com', 'www.example.COM', true],
    +        ];
    +    }
    +
    +    /**
    +     * @dataProvider getDomainMatchesProvider
    +     */
    +    public function testIgnoresCookiesForMismatchingDomains(string $requestHost, string $domainAttribute, bool $matches)
    +    {
    +        $response = (new Response(200))
    +            ->withAddedHeader(
    +                'Set-Cookie',
    +                "foo=bar; expires={$this->futureExpirationDate()}; domain={$domainAttribute}; path=/;"
    +            )
    +        ;
    +        $request = (new Request('GET', "https://{$requestHost}/"));
    +        $this->jar->extractCookies($request, $response);
    +
    +        self::assertCount($matches ? 1 : 0, $this->jar->toArray());
    +    }
    +
         private function futureExpirationDate()
         {
             return (new DateTimeImmutable)->add(new DateInterval('P1D'))->format(DateTime::COOKIE);
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

8

News mentions

0

No linked articles in our index yet.