Skip to content

2.7.18.16: urllib2 HTTPPasswordMgr sends https credentials over http (CVE-2026-15806) - #102

Merged
icanhasmath merged 1 commit into
2.7.18.16from
2.7.18.16-urllib2-passwordmgr-scheme
Oct 28, 2026
Merged

icanhasmath merged 1 commit into
2.7.18.16from
2.7.18.16-urllib2-passwordmgr-scheme

Conversation

@icanhasmath

Copy link
Copy Markdown

Backport of python/cpython pythongh-155694 (CVE-2026-15806, PSF-2026-36) to 2.7.18.x.

Problem: urllib2.HTTPPasswordMgr and HTTPPasswordMgrWithDefaultRealm dropped the URL scheme when matching, so credentials stored for https://example.com/ were also used for http://example.com/. Anyone who could downgrade or redirect the client to plain HTTP could capture credentials meant for TLS.

Fix: a stored URI that includes a scheme now only matches requests with the same scheme. A stored bare authority (example.com) or //host/ reference still matches any scheme. reduce_uri() and is_suburi() behave as before.

Behavior change: credentials stored for https://… are no longer sent to http://…. This is the intended security fix, but code that relied on it needs to also add the http:// URI or a bare authority.

Tests: PasswordManagerSchemeTests, which adds a default-realm case to upstream's tests.

  • Two of the three new tests fail on 2.7.18.15.
  • With the fix, test_urllib2 passes 52/52 and the existing doctests pass (66 + 11).
  • test_urllib2_localnet, test_urllib2net and test_xmlrpc pass.

Part of 2.7.18.16. It targets the 2.7.18.16 integration branch.

…-2026-15806)

Backport of python/cpython pythongh-155694 (3.14 a0d023f) to 2.7.18.x.

HTTPPasswordMgr (and HTTPPasswordMgrWithDefaultRealm) reduced URIs to
(authority, path) and dropped the scheme, so credentials added for
https://example.com/ were also returned for http://example.com/.  An
attacker who can redirect or downgrade a client to plain HTTP could then
capture Basic/Digest credentials meant only for TLS.  Stored and
requested URIs now keep their scheme, and a stored URI with a scheme
only matches requests with the same scheme.  A stored bare authority
("example.com") or network-path reference ("//example.com/") still
matches any scheme, as before; reduce_uri() and is_suburi() keep their
public behavior.

2.7.18.x: 2.7 has no HTTPPasswordMgrWithPriorAuth, so only
add_password()/find_user_password() change.  (scheme,) + tuple replaces
the 3.x star-unpacking.  2.7's password-manager tests are doctests, so
the new tests go in a PasswordManagerSchemeTests class registered in
test_main(), with an extra HTTPPasswordMgrWithDefaultRealm case.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

@ezequielp-activestate ezequielp-activestate left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blockers. The change is upstream's pythongh-155694 fix (a7bb524), minus the HTTPPasswordMgrWithPriorAuth part, since that class doesn't exist in 2.7. 2.7's urlparse.urlsplit lower-cases the scheme and returns no netloc for a bare host or host:port, so a bare authority still matches any scheme.

  • Repro: add_password("R", "https://example.com/", ...), then find_user_password("R", "http://example.com/"):
    • On the base, it returns the credentials, for both HTTPPasswordMgr and HTTPPasswordMgrWithDefaultRealm.
    • On this branch, it returns (None, None), and https://example.com/sub still returns them.
    • A bare example.com entry still matches http://example.com/ and the scheme-less authority ProxyBasicAuthHandler passes.
  • Tests: test_urllib2 and test_urllib2_localnet pass. The new tests fail against the base urllib2.py.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants