CVE-2018-20060
Description
urllib3 before version 1.23 does not remove the Authorization HTTP header when following a cross-origin redirect (i.e., a redirect that differs in host, port, or scheme). This can allow for credentials in the Authorization header to be exposed to unintended hosts or transmitted in cleartext.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
urllib3 before 1.23 fails to strip the Authorization header on cross-origin redirects, potentially exposing credentials to unintended hosts or cleartext.
Vulnerability
urllib3 versions before 1.23 do not remove the Authorization HTTP header when following a cross-origin redirect (i.e., a redirect that differs in host, port, or scheme) [1][3]. This flaw exists in the handling of redirects where the Retry.remove_headers_on_redirect list was not applied for ProxyManager.connection_from_url under the same conditions [1]. The affected versions are all urllib3 releases prior to 1.23 [3].
Exploitation
An attacker must be in a position to intercept traffic between a victim and a legitimate server [4]. The victim must visit an HTTPS server that issues a redirect (e.g., to an HTTP endpoint on a different host, port, or scheme) [4]. The attack requires the legitimate server to have such a cross-origin redirect configured and the user to follow it; the attacker then captures the Authorization header transmitted in the redirect [4]. Attack complexity is high because it depends on both the specific application's behavior and a man-in-the-middle position [4].
Impact
If successfully exploited, the attacker gains access to the credentials sent in the Authorization header (e.g., Basic Authentication tokens or Bearer tokens) [1][3][4]. This can lead to disclosure of sensitive authentication information, which may be reused to impersonate the user on the original host or other services where the same credentials are valid [3]. The confidentiality of the credential is compromised, but no other system impact is directly caused by this flaw.
Mitigation
urllib3 version 1.23 and later remove the Authorization header by default on all cross-origin redirects [4]. Users should upgrade to urllib3 >= 1.23 as soon as possible [1][2]. As a workaround, if upgrading is not immediately feasible, applications can disable automatic redirects by passing `retries=urllib3.Retry(redirect=0)” when performing requests and handle redirects manually [4]. Red Hat has released an erratum (RHSA-2019:2272) for RHEL [2]. No known exploitation in the wild was reported at the time of publication.
AI Insight generated on May 22, 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.
| Package | Affected versions | Patched versions |
|---|---|---|
urllib3PyPI | < 1.23 | 1.23 |
Affected products
40- ghsa-coords40 versionspkg:pypi/urllib3pkg:rpm/almalinux/python2-attrspkg:rpm/almalinux/python2-chardetpkg:rpm/almalinux/python2-coveragepkg:rpm/almalinux/python2-Cythonpkg:rpm/almalinux/python2-dnspkg:rpm/almalinux/python2-docspkg:rpm/almalinux/python2-docs-infopkg:rpm/almalinux/python2-docutilspkg:rpm/almalinux/python2-funcsigspkg:rpm/almalinux/python2-idnapkg:rpm/almalinux/python2-ipaddresspkg:rpm/almalinux/python2-markupsafepkg:rpm/almalinux/python2-mockpkg:rpm/almalinux/python2-pluggypkg:rpm/almalinux/python2-psycopg2pkg:rpm/almalinux/python2-psycopg2-debugpkg:rpm/almalinux/python2-psycopg2-testspkg:rpm/almalinux/python2-pypkg:rpm/almalinux/python2-PyMySQLpkg:rpm/almalinux/python2-pysockspkg:rpm/almalinux/python2-pytestpkg:rpm/almalinux/python2-pytest-mockpkg:rpm/almalinux/python2-pytzpkg:rpm/almalinux/python2-pyyamlpkg:rpm/almalinux/python2-requestspkg:rpm/almalinux/python2-rpm-macrospkg:rpm/almalinux/python2-setuptools_scmpkg:rpm/almalinux/python-psycopg2-docpkg:rpm/opensuse/python-urllib3&distro=openSUSE%20Leap%2015.0pkg:rpm/opensuse/python-urllib3&distro=openSUSE%20Tumbleweedpkg:rpm/suse/python-urllib3&distro=HPE%20Helion%20OpenStack%208pkg:rpm/suse/python-urllib3&distro=SUSE%20Enterprise%20Storage%204pkg:rpm/suse/python-urllib3&distro=SUSE%20Enterprise%20Storage%205pkg:rpm/suse/python-urllib3&distro=SUSE%20Linux%20Enterprise%20Module%20for%20Basesystem%2015pkg:rpm/suse/python-urllib3&distro=SUSE%20Linux%20Enterprise%20Module%20for%20Public%20Cloud%2012pkg:rpm/suse/python-urllib3&distro=SUSE%20Manager%20Server%203.2pkg:rpm/suse/python-urllib3&distro=SUSE%20OpenStack%20Cloud%207pkg:rpm/suse/python-urllib3&distro=SUSE%20OpenStack%20Cloud%208pkg:rpm/suse/python-urllib3&distro=SUSE%20OpenStack%20Cloud%20Crowbar%208
< 1.23+ 39 more
- (no CPE)range: < 1.23
- (no CPE)range: < 17.4.0-10.module_el8.6.0+2781+fed64c13
- (no CPE)range: < 3.0.4-10.module_el8.6.0+2781+fed64c13
- (no CPE)range: < 4.5.1-4.module_el8.6.0+2781+fed64c13
- (no CPE)range: < 0.28.1-7.module_el8.6.0+2781+fed64c13
- (no CPE)range: < 1.15.0-10.module_el8.6.0+2781+fed64c13
- (no CPE)range: < 2.7.16-2.module_el8.6.0+2781+fed64c13
- (no CPE)range: < 2.7.16-2.module_el8.6.0+2781+fed64c13
- (no CPE)range: < 0.14-12.module_el8.6.0+2781+fed64c13
- (no CPE)range: < 1.0.2-13.module_el8.6.0+2781+fed64c13
- (no CPE)range: < 2.5-7.module_el8.6.0+2781+fed64c13
- (no CPE)range: < 1.0.18-6.module_el8.6.0+2781+fed64c13
- (no CPE)range: < 0.23-19.module_el8.6.0+2781+fed64c13
- (no CPE)range: < 2.0.0-13.module_el8.6.0+2781+fed64c13
- (no CPE)range: < 0.6.0-8.module_el8.6.0+2781+fed64c13
- (no CPE)range: < 2.7.5-7.module_el8.6.0+2781+fed64c13
- (no CPE)range: < 2.7.5-7.module_el8.6.0+2781+fed64c13
- (no CPE)range: < 2.7.5-7.module_el8.6.0+2781+fed64c13
- (no CPE)range: < 1.5.3-6.module_el8.6.0+2781+fed64c13
- (no CPE)range: < 0.8.0-10.module_el8.6.0+2781+fed64c13
- (no CPE)range: < 1.6.8-6.module_el8.6.0+2781+fed64c13
- (no CPE)range: < 3.4.2-13.module_el8.6.0+2781+fed64c13
- (no CPE)range: < 1.9.0-4.module_el8.6.0+2781+fed64c13
- (no CPE)range: < 2017.2-12.module_el8.6.0+2781+fed64c13
- (no CPE)range: < 3.12-16.module_el8.6.0+2781+fed64c13
- (no CPE)range: < 2.20.0-3.module_el8.6.0+2781+fed64c13
- (no CPE)range: < 3-38.module_el8.6.0+2781+fed64c13
- (no CPE)range: < 1.15.7-6.module_el8.6.0+2781+fed64c13
- (no CPE)range: < 2.7.5-7.module_el8.6.0+2781+fed64c13
- (no CPE)range: < 1.22-lp150.5.3.1
- (no CPE)range: < 1.26.6-1.2
- (no CPE)range: < 1.22-5.6.1
- (no CPE)range: < 1.22-3.14.1
- (no CPE)range: < 1.22-3.14.1
- (no CPE)range: < 1.22-6.4.1
- (no CPE)range: < 1.22-3.14.1
- (no CPE)range: < 1.22-3.14.1
- (no CPE)range: < 1.16-3.6.1
- (no CPE)range: < 1.22-5.6.1
- (no CPE)range: < 1.22-5.6.1
Patches
1560bd227b90fRemove Authorization header when redirecting cross-host (#1346)
5 files changed · +81 −2
CHANGES.rst+4 −0 modified@@ -4,6 +4,10 @@ Changes dev (master) ------------ +* Allow providing a list of headers to strip from requests when redirecting + to a different host. Defaults to the ``Authorization`` header. Different + headers can be set via ``Retry.remove_headers_on_redirect``. (Issue #1316) + * Fix ``util.selectors._fileobj_to_fd`` to accept ``long`` (Issue #1247). * Dropped Python 3.3 support. (Pull #1242)
test/test_retry.py+10 −0 modified@@ -249,3 +249,13 @@ def test_retry_method_not_in_whitelist(self): retry = Retry() with pytest.raises(ReadTimeoutError): retry.increment(method='POST', error=error) + + def test_retry_default_remove_headers_on_redirect(self): + retry = Retry() + + assert list(retry.remove_headers_on_redirect) == ['Authorization'] + + def test_retry_set_remove_headers_on_redirect(self): + retry = Retry(remove_headers_on_redirect=['X-API-Secret']) + + assert list(retry.remove_headers_on_redirect) == ['X-API-Secret']
test/with_dummyserver/test_poolmanager.py+46 −0 modified@@ -109,6 +109,52 @@ def test_too_many_redirects(self): except MaxRetryError: pass + def test_redirect_cross_host_remove_headers(self): + http = PoolManager() + self.addCleanup(http.clear) + + r = http.request('GET', '%s/redirect' % self.base_url, + fields={'target': '%s/headers' % self.base_url_alt}, + headers={'Authorization': 'foo'}) + + self.assertEqual(r.status, 200) + + data = json.loads(r.data.decode('utf-8')) + + self.assertNotIn('Authorization', data) + + def test_redirect_cross_host_no_remove_headers(self): + http = PoolManager() + self.addCleanup(http.clear) + + r = http.request('GET', '%s/redirect' % self.base_url, + fields={'target': '%s/headers' % self.base_url_alt}, + headers={'Authorization': 'foo'}, + retries=Retry(remove_headers_on_redirect=[])) + + self.assertEqual(r.status, 200) + + data = json.loads(r.data.decode('utf-8')) + + self.assertEqual(data['Authorization'], 'foo') + + def test_redirect_cross_host_set_removed_headers(self): + http = PoolManager() + self.addCleanup(http.clear) + + r = http.request('GET', '%s/redirect' % self.base_url, + fields={'target': '%s/headers' % self.base_url_alt}, + headers={'X-API-Secret': 'foo', + 'Authorization': 'bar'}, + retries=Retry(remove_headers_on_redirect=['X-API-Secret'])) + + self.assertEqual(r.status, 200) + + data = json.loads(r.data.decode('utf-8')) + + self.assertNotIn('X-API-Secret', data) + self.assertEqual(data['Authorization'], 'bar') + def test_raise_on_redirect(self): http = PoolManager() self.addCleanup(http.clear)
urllib3/poolmanager.py+10 −1 modified@@ -312,8 +312,9 @@ def urlopen(self, method, url, redirect=True, **kw): kw['assert_same_host'] = False kw['redirect'] = False + if 'headers' not in kw: - kw['headers'] = self.headers + kw['headers'] = self.headers.copy() if self.proxy is not None and u.scheme == "http": response = conn.urlopen(method, url, **kw) @@ -335,6 +336,14 @@ def urlopen(self, method, url, redirect=True, **kw): if not isinstance(retries, Retry): retries = Retry.from_int(retries, redirect=redirect) + # Strip headers marked as unsafe to forward to the redirected location. + # Check remove_headers_on_redirect to avoid a potential network call within + # conn.is_same_host() which may use socket.gethostbyname() in the future. + if (retries.remove_headers_on_redirect + and not conn.is_same_host(redirect_location)): + for header in retries.remove_headers_on_redirect: + kw['headers'].pop(header, None) + try: retries = retries.increment(method, url, response=response, _pool=conn) except MaxRetryError:
urllib3/util/retry.py+11 −1 modified@@ -19,6 +19,7 @@ log = logging.getLogger(__name__) + # Data structure for representing the metadata of requests that result in a retry. RequestHistory = namedtuple('RequestHistory', ["method", "url", "error", "status", "redirect_location"]) @@ -139,20 +140,27 @@ class Retry(object): Whether to respect Retry-After header on status codes defined as :attr:`Retry.RETRY_AFTER_STATUS_CODES` or not. + :param iterable remove_headers_on_redirect: + Sequence of headers to remove from the request when a response + indicating a redirect is returned before firing off the redirected + request. """ DEFAULT_METHOD_WHITELIST = frozenset([ 'HEAD', 'GET', 'PUT', 'DELETE', 'OPTIONS', 'TRACE']) RETRY_AFTER_STATUS_CODES = frozenset([413, 429, 503]) + DEFAULT_REDIRECT_HEADERS_BLACKLIST = frozenset(['Authorization']) + #: Maximum backoff time. BACKOFF_MAX = 120 def __init__(self, total=10, connect=None, read=None, redirect=None, status=None, method_whitelist=DEFAULT_METHOD_WHITELIST, status_forcelist=None, backoff_factor=0, raise_on_redirect=True, raise_on_status=True, - history=None, respect_retry_after_header=True): + history=None, respect_retry_after_header=True, + remove_headers_on_redirect=DEFAULT_REDIRECT_HEADERS_BLACKLIST): self.total = total self.connect = connect @@ -171,6 +179,7 @@ def __init__(self, total=10, connect=None, read=None, redirect=None, status=None self.raise_on_status = raise_on_status self.history = history or tuple() self.respect_retry_after_header = respect_retry_after_header + self.remove_headers_on_redirect = remove_headers_on_redirect def new(self, **kw): params = dict( @@ -182,6 +191,7 @@ def new(self, **kw): raise_on_redirect=self.raise_on_redirect, raise_on_status=self.raise_on_status, history=self.history, + remove_headers_on_redirect=self.remove_headers_on_redirect ) params.update(kw) return type(self)(**params)
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
23- lists.opensuse.org/opensuse-security-announce/2019-09/msg00039.htmlghsavendor-advisoryx_refsource_SUSEWEB
- access.redhat.com/errata/RHSA-2019:2272ghsavendor-advisoryx_refsource_REDHATWEB
- github.com/advisories/GHSA-www2-v7xj-xrc6ghsaADVISORY
- lists.fedoraproject.org/archives/list/package-announce%40lists.fedoraproject.org/message/5SJERZEJDSUYQP7BNBXMBHRHGY26HRZD/mitrevendor-advisoryx_refsource_FEDORA
- lists.fedoraproject.org/archives/list/package-announce%40lists.fedoraproject.org/message/BXLAXHM3Z6DUCXZ7ZXZ2EAYJXWDCZFCT/mitrevendor-advisoryx_refsource_FEDORA
- lists.fedoraproject.org/archives/list/package-announce%40lists.fedoraproject.org/message/XWP36YW3KSVLXDBY3QJKDYEPCIMN3VQZ/mitrevendor-advisoryx_refsource_FEDORA
- nvd.nist.gov/vuln/detail/CVE-2018-20060ghsaADVISORY
- usn.ubuntu.com/3990-1/mitrevendor-advisoryx_refsource_UBUNTU
- bugzilla.redhat.com/show_bug.cgighsax_refsource_MISCWEB
- github.com/pypa/advisory-database/tree/main/vulns/urllib3/PYSEC-2018-32.yamlghsaWEB
- github.com/urllib3/urllib3/blob/master/CHANGES.rstghsax_refsource_MISCWEB
- github.com/urllib3/urllib3/commit/560bd227b90f74417ffaedebf5f8d05a8ee4f532ghsaWEB
- github.com/urllib3/urllib3/issues/1316ghsax_refsource_MISCWEB
- github.com/urllib3/urllib3/pull/1346ghsax_refsource_MISCWEB
- lists.debian.org/debian-lts-announce/2021/06/msg00015.htmlghsamailing-listx_refsource_MLISTWEB
- lists.fedoraproject.org/archives/list/package-announce%40lists.fedoraproject.org/message/5SJERZEJDSUYQP7BNBXMBHRHGY26HRZDghsaWEB
- lists.fedoraproject.org/archives/list/package-announce%40lists.fedoraproject.org/message/BXLAXHM3Z6DUCXZ7ZXZ2EAYJXWDCZFCTghsaWEB
- lists.fedoraproject.org/archives/list/package-announce%40lists.fedoraproject.org/message/XWP36YW3KSVLXDBY3QJKDYEPCIMN3VQZghsaWEB
- lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/5SJERZEJDSUYQP7BNBXMBHRHGY26HRZDghsaWEB
- lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/BXLAXHM3Z6DUCXZ7ZXZ2EAYJXWDCZFCTghsaWEB
- lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/XWP36YW3KSVLXDBY3QJKDYEPCIMN3VQZghsaWEB
- security.netapp.com/advisory/ntap-20241227-0010ghsaWEB
- usn.ubuntu.com/3990-1ghsaWEB
News mentions
0No linked articles in our index yet.