Wrkzeug's incorrect parsing of nameless cookies leads to __Host- cookies bypass
Description
Werkzeug is a comprehensive WSGI web application library. Browsers may allow "nameless" cookies that look like =value instead of key=value. A vulnerable browser may allow a compromised application on an adjacent subdomain to exploit this to set a cookie like =__Host-test=bad for another subdomain. Werkzeug prior to 2.2.3 will parse the cookie =__Host-test=bad as __Host-test=bad`. If a Werkzeug application is running next to a vulnerable or malicious subdomain which sets such a cookie using a vulnerable browser, the Werkzeug application will see the bad cookie value but the valid cookie key. The issue is fixed in Werkzeug 2.2.3.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
WerkzeugPyPI | < 2.2.3 | 2.2.3 |
Affected products
1Patches
1cf275f42acadMerge pull request from GHSA-px8h-6qxv-m22q
4 files changed · +14 −9
CHANGES.rst+2 −0 modified@@ -19,6 +19,8 @@ Unreleased :issue:`2529` - ``LimitedStream.read`` works correctly when wrapping a stream that may not return the requested size in one ``read`` call. :issue:`2558` +- A cookie header that starts with ``=`` is treated as an empty key and discarded, + rather than stripping the leading ``==``. Version 2.2.2
src/werkzeug/_internal.py+9 −4 modified@@ -34,7 +34,7 @@ _legal_cookie_chars_re = rb"[\w\d!#%&\'~_`><@,:/\$\*\+\-\.\^\|\)\(\?\}\{\=]" _cookie_re = re.compile( rb""" - (?P<key>[^=;]+) + (?P<key>[^=;]*) (?:\s*=\s* (?P<val> "(?:[^\\"]|\\.)*" | @@ -382,16 +382,21 @@ def _cookie_parse_impl(b: bytes) -> t.Iterator[t.Tuple[bytes, bytes]]: """Lowlevel cookie parsing facility that operates on bytes.""" i = 0 n = len(b) + b += b";" while i < n: - match = _cookie_re.search(b + b";", i) + match = _cookie_re.match(b, i) + if not match: break - key = match.group("key").strip() - value = match.group("val") or b"" i = match.end(0) + key = match.group("key").strip() + + if not key: + continue + value = match.group("val") or b"" yield key, _cookie_unquote(value)
src/werkzeug/sansio/http.py+0 −4 modified@@ -126,10 +126,6 @@ def parse_cookie( def _parse_pairs() -> t.Iterator[t.Tuple[str, str]]: for key, val in _cookie_parse_impl(cookie): # type: ignore key_str = _to_str(key, charset, errors, allow_none_charset=True) - - if not key_str: - continue - val_str = _to_str(val, charset, errors, allow_none_charset=True) yield key_str, val_str
tests/test_http.py+3 −1 modified@@ -412,7 +412,8 @@ def test_is_resource_modified_for_range_requests(self): def test_parse_cookie(self): cookies = http.parse_cookie( "dismiss-top=6; CP=null*; PHPSESSID=0a539d42abc001cdc762809248d4beed;" - 'a=42; b="\\";"; ; fo234{=bar;blub=Blah; "__Secure-c"=d' + 'a=42; b="\\";"; ; fo234{=bar;blub=Blah; "__Secure-c"=d;' + "==__Host-eq=bad;__Host-eq=good;" ) assert cookies.to_dict() == { "CP": "null*", @@ -423,6 +424,7 @@ def test_parse_cookie(self): "fo234{": "bar", "blub": "Blah", '"__Secure-c"': "d", + "__Host-eq": "good", } def test_dump_cookie(self):
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
9- github.com/advisories/GHSA-px8h-6qxv-m22qghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2023-23934ghsaADVISORY
- github.com/pallets/werkzeug/commit/cf275f42acad1b5950c50ffe8ef58fe62cdce028ghsax_refsource_MISCWEB
- github.com/pallets/werkzeug/releases/tag/2.2.3ghsax_refsource_MISCWEB
- github.com/pallets/werkzeug/security/advisories/GHSA-px8h-6qxv-m22qghsax_refsource_CONFIRMWEB
- github.com/pypa/advisory-database/tree/main/vulns/werkzeug/PYSEC-2023-57.yamlghsaWEB
- security.netapp.com/advisory/ntap-20230818-0003ghsaWEB
- www.debian.org/security/2023/dsa-5470ghsaWEB
- security.netapp.com/advisory/ntap-20230818-0003/mitre
News mentions
0No linked articles in our index yet.