VYPR
Moderate severityNVD Advisory· Published Oct 15, 2023· Updated Sep 16, 2024

CVE-2018-25091

CVE-2018-25091

Description

urllib3 before 1.24.2 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. NOTE: this issue exists because of an incomplete fix for CVE-2018-20060 (which was case-sensitive).

AI Insight

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

Urllib3 before 1.24.2 improperly retains the Authorization header on cross-origin redirects, exposing credentials to unintended hosts.

Vulnerability

Overview

CVE-2018-25091 is a security flaw in urllib3, the popular Python HTTP client library, affecting versions prior to 1.24.2. The vulnerability arises because the library does not remove the Authorization HTTP header when following a cross-origin redirect, i.e., a redirect that changes the host, port, or scheme [1]. This behavior can leak sensitive credentials (such as Basic Auth tokens) to unintended servers or even transmit them in cleartext over unencrypted connections. Notably, this issue exists due to an incomplete fix for CVE-2018-20060, which used a case-sensitive header removal that could be bypassed [1].

Attack

Vector and Exploitation

An attacker can exploit this by setting up a server that issues an HTTP redirect to a different origin; when the victim's application (using a vulnerable urllib3 version) follows that redirect, the Authorization header is forwarded to the attacker-controlled host. No additional authentication is needed from the attacker beyond being able to craft a redirect response. The fix, introduced in commit adb358f, makes the header removal case-insensitive by converting all headers to lowercase before checking against the removal list, preventing the bypass [2].

Impact and

Mitigation

If exploited, an attacker can obtain credentials (e.g., username and password) that were intended for a legitimate service, potentially leading to account compromise or unauthorized access to protected resources. The vulnerability is scored with CVSS v4.0 metrics not yet provided by NVD, but the issue is classified as a high-severity information disclosure [1]. Users are strongly advised to upgrade to urllib3 version 1.24.2 or later. As of this writing, there are no known workarounds beyond updating the library [1][2].

AI Insight generated on May 20, 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
urllib3PyPI
< 1.24.21.24.2

Affected products

4

Patches

1
adb358f8e068

Remove Authorization headers regardless of case on cross-origin redirects (#1511)

https://github.com/urllib3/urllib3YOSHIDA KatsuhikoDec 29, 2018via ghsa
6 files changed · +39 5
  • CHANGES.rst+2 0 modified
    @@ -8,6 +8,8 @@ dev (master)
     
     * Upgraded ``urllib3.utils.parse_url()`` to be RFC 3986 compliant. (Pull #1487)
     
    +* Remove Authorization header regardless of case when redirecting to cross-site. (Issue #1510)
    +
     * ... [Short description of non-trivial change.] (Issue #)
     
     
    
  • CONTRIBUTORS.txt+3 0 modified
    @@ -272,5 +272,8 @@ In chronological order:
     * Justin Bramley <https://github.com/jbramleycl>
       * Add ability to handle multiple Content-Encodings
     
    +* Katsuhiko YOSHIDA <https://github.com/kyoshidajp>
    +  * Remove Authorization header regardless of case when redirecting to cross-site
    +
     * [Your name or handle] <[email or website]>
       * [Brief summary of your changes]
    
  • src/urllib3/poolmanager.py+5 2 modified
    @@ -7,6 +7,7 @@
     from .connectionpool import HTTPConnectionPool, HTTPSConnectionPool
     from .connectionpool import port_by_scheme
     from .exceptions import LocationValueError, MaxRetryError, ProxySchemeUnknown
    +from .packages import six
     from .packages.six.moves.urllib.parse import urljoin
     from .request import RequestMethods
     from .util.url import parse_url
    @@ -342,8 +343,10 @@ def urlopen(self, method, url, redirect=True, **kw):
             # 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)
    +            headers = list(six.iterkeys(kw['headers']))
    +            for header in headers:
    +                if header.lower() in retries.remove_headers_on_redirect:
    +                    kw['headers'].pop(header, None)
     
             try:
                 retries = retries.increment(method, url, response=response, _pool=conn)
    
  • src/urllib3/util/retry.py+2 1 modified
    @@ -179,7 +179,8 @@ 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
    +        self.remove_headers_on_redirect = frozenset([
    +            h.lower() for h in remove_headers_on_redirect])
     
         def new(self, **kw):
             params = dict(
    
  • test/test_retry.py+2 2 modified
    @@ -253,9 +253,9 @@ def test_retry_method_not_in_whitelist(self):
         def test_retry_default_remove_headers_on_redirect(self):
             retry = Retry()
     
    -        assert list(retry.remove_headers_on_redirect) == ['Authorization']
    +        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']
    +        assert list(retry.remove_headers_on_redirect) == ['x-api-secret']
    
  • test/with_dummyserver/test_poolmanager.py+25 0 modified
    @@ -123,6 +123,17 @@ def test_redirect_cross_host_remove_headers(self):
     
             self.assertNotIn('Authorization', data)
     
    +        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)
    +        self.assertNotIn('Authorization', data)
    +
         def test_redirect_cross_host_no_remove_headers(self):
             http = PoolManager()
             self.addCleanup(http.clear)
    @@ -155,6 +166,20 @@ def test_redirect_cross_host_set_removed_headers(self):
             self.assertNotIn('X-API-Secret', data)
             self.assertEqual(data['Authorization'], 'bar')
     
    +        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.assertNotIn('X-API-Secret', data)
    +        self.assertEqual(data['Authorization'], 'bar')
    +
         def test_raise_on_redirect(self):
             http = PoolManager()
             self.addCleanup(http.clear)
    

Vulnerability mechanics

Synthesis attempt was rejected by the grounding validator. Re-run pending.

References

6

News mentions

0

No linked articles in our index yet.