{"id":"CVE-2026-45300","aliases":["GHSA-fmxf-pm6p-7xgm"],"url":"https://o3.security/vulnerability/CVE-2026-45300","summary":"async-http-client: Cookie header not stripped on cross-origin redirect","details":"## Summary\n\nasync-http-client leaks `Cookie` headers to cross-origin redirect targets. When following a redirect across a security boundary (different origin, or HTTPS→HTTP downgrade), the `propagatedHeaders()` method in `Redirect30xInterceptor.java` strips `Authorization` and `Proxy-Authorization` headers but does not strip `Cookie`, so session cookies and other sensitive cookie values are forwarded to the redirect target — which may be attacker-controlled.\n\n## Details\n\nThe vulnerability is in `client/src/main/java/org/asynchttpclient/netty/handler/intercept/Redirect30xInterceptor.java`.\n\nThe caller computes `stripAuth` on each redirect:\n\n```java\nboolean sameBase    = request.getUri().isSameBase(newUri);\nboolean stripAuth   = !sameBase || schemeDowngrade || stripAuthorizationOnRedirect;\n// ...\nrequestBuilder.setHeaders(propagatedHeaders(request, realm, keepBody, stripAuth));\n```\n\n`stripAuth` is `true` whenever the redirect crosses an origin, downgrades the scheme, or the caller opted in via `AsyncHttpClientConfig#isStripAuthorizationOnRedirect()`.\n\nIn the vulnerable version, `propagatedHeaders()` only removes `Authorization` and `Proxy-Authorization` in that branch — `Cookie` is left untouched:\n\n```java\nprivate static HttpHeaders propagatedHeaders(Request request, Realm realm, boolean keepBody, boolean stripAuthorization) {\n    HttpHeaders headers = request.getHeaders()\n            .remove(HOST)\n            .remove(CONTENT_LENGTH);\n\n    if (!keepBody) {\n        headers.remove(CONTENT_TYPE);\n    }\n\n    if (stripAuthorization || (realm != null && (realm.getScheme() == AuthScheme.NTLM\n            || realm.getScheme() == AuthScheme.SCRAM_SHA_256))) {\n        headers.remove(AUTHORIZATION)\n                .remove(PROXY_AUTHORIZATION);\n        // BUG: COOKIE is not removed here, so cookies leak across the security boundary.\n    }\n    return headers;\n}\n```\n\nThe companion test class `RedirectCredentialSecurityTest` covers `Authorization` / `Proxy-Authorization` stripping on cross-origin redirects and scheme downgrades, but has no coverage for `Cookie`, which is why the regression went unnoticed.\n\n## Proof of concept\n\n```java\nimport org.asynchttpclient.*;\n\nAsyncHttpClient client = asyncHttpClient();\n\n// trusted-api.com responds 302 -> https://evil.com\nRequest request = new RequestBuilder(\"GET\")\n        .setUrl(\"https://trusted-api.com/endpoint\")\n        .setHeader(\"Cookie\", \"session=abc123; csrf=xyz789; api_key=secret\")\n        .setHeader(\"Authorization\", \"Bearer token123\")\n        .build();\n\nclient.executeRequest(request).get();\n\n// Request seen by evil.com after the redirect:\n//   Authorization: <stripped>\n//   Cookie:        session=abc123; csrf=xyz789; api_key=secret   <-- leaked\n```\n\n## Impact\n\n- **Session hijacking** — leaked session cookies allow impersonation.\n- **CSRF token theft** — CSRF tokens carried in cookies are disclosed.\n- **API key theft** — API keys stored in cookies are disclosed.\n- **Privacy** — tracking identifiers leak to third-party origins.\n\nRealistic attack paths:\n\n- Open-redirect in a trusted API endpoint.\n- Compromised CDN or API gateway injecting redirects.\n- MITM on a plaintext hop in the redirect chain.\n\n## Fix\n\nAdd `COOKIE` to the headers removed alongside `AUTHORIZATION` / `PROXY_AUTHORIZATION` on the security-boundary branch:\n\n```java\nif (stripAuthorization) {\n    headers.remove(AUTHORIZATION)\n            .remove(PROXY_AUTHORIZATION)\n            .remove(COOKIE);\n} else if (realm != null && (realm.getScheme() == AuthScheme.NTLM\n        || realm.getScheme() == AuthScheme.SCRAM_SHA_256)) {\n    headers.remove(AUTHORIZATION)\n            .remove(PROXY_AUTHORIZATION);\n}\n```\n\nNote that the URI-scoped `CookieStore` will re-add any cookies that legitimately match the new target after `propagatedHeaders` returns, so legitimate cross-origin sessions tracked by the client are not broken.\n\nFixed in **3.0.10** and **2.15.0** by commit [`3b0e3e9e`](https://github.com/AsyncHttpClient/async-http-client/commit/3b0e3e9e).","published":"2026-06-05T19:32:43.547Z","modified":"2026-08-12T03:51:26.327452941Z","cvss":{"score":7.4,"severity":"HIGH","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:N/A:N"},"epss":{"score":0.00322,"percentile":0.24435,"asOf":"2026-08-24"},"cisaKev":null,"exploitsKnown":0,"affectedPackages":[{"ecosystem":"Maven","name":"org.asynchttpclient:async-http-client","fixedVersion":"3.0.10"},{"ecosystem":"Maven","name":"org.asynchttpclient:async-http-client","fixedVersion":"2.15.0"}],"fix":{"url":"https://github.com/AsyncHttpClient/async-http-client/commit/3b0e3e9e","label":"AsyncHttpClient/async-http-client@3b0e3e9"},"references":[{"type":"WEB","url":"https://github.com/AsyncHttpClient/async-http-client/releases/tag/async-http-client-project-3.0.10"},{"type":"ADVISORY","url":"https://github.com/AsyncHttpClient/async-http-client/security/advisories/GHSA-fmxf-pm6p-7xgm"},{"type":"ADVISORY","url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/45xxx/CVE-2026-45300.json"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-45300"},{"type":"FIX","url":"https://github.com/AsyncHttpClient/async-http-client/commit/3b0e3e9e"},{"type":"PACKAGE","url":"https://github.com/AsyncHttpClient/async-http-client"}],"provenance":{"sources":["OSV.dev","FIRST.org (EPSS)"],"lastVerified":"2026-08-12T03:51:26.327452941Z"}}