CVE-2026-97687 Overview
CVE-2026-97687 is an improper certificate validation vulnerability [CWE-295] in urllib3, a widely used HTTP client library for Python. Versions 1.26.0 through 2.7.x fail to isolate target-server TLS settings from the HTTPS proxy handshake. When an application uses an HTTPS forwarding proxy, configuration options such as proxy_ssl_context, proxy_assert_hostname, proxy_assert_fingerprint, ssl_context, cert_reqs, verify_mode, and use_forwarding_for_https=True bleed across contexts. Setting cert_reqs=CERT_NONE on a target request can overwrite proxy_ssl_context.verify_mode in place, and the mutation persists for later connections reusing the context. The issue is fixed in urllib3 2.8.0.
Critical Impact
An attacker who can position themselves on the network path can impersonate the HTTPS proxy, observe or modify forwarded HTTP traffic, and receive target TLS client certificates once proxy verification is disabled.
Affected Products
- urllib3 versions 1.26.0 through 2.7.x (fixed in 2.8.0)
- Python applications using an HTTPS proxy with use_forwarding_for_https=True
- Applications that mix per-request cert_reqs=CERT_NONE with a shared proxy_ssl_context
Discovery Timeline
- 2026-09-29 - CVE-2026-97687 published to NVD
- 2026-09-29 - Last updated in NVD database
- urllib3 2.8.0 - Fix released via GitHub Release 2.8.0 and GHSA-8988-9cw3-xx77
Technical Details for CVE-2026-97687
Vulnerability Analysis
urllib3 supports two HTTPS-proxy modes: CONNECT tunneling and HTTPS forwarding. In forwarding mode, the client establishes a TLS session with the proxy and forwards the plaintext HTTP request over it. The proxy TLS handshake must be governed by proxy-specific policy, while target-server TLS settings apply only inside a tunneled end-to-end connection.
The vulnerable code paths in HTTPSConnection.connect() applied target Server Name Indication (SNI), hostname assertions, fingerprint assertions, and client credentials to the forwarding proxy handshake. When cert_reqs=CERT_NONE is passed for the target, urllib3 mutated proxy_ssl_context.verify_mode in place. The context is shared across the pool, so subsequent connections reused a context with certificate verification disabled against the proxy.
CONNECT tunneling remains safe because the end-to-end TLS session to the target is negotiated separately after the tunnel is established.
Root Cause
The root cause is a failure to keep the proxy SSLContext and the target SSLContext as separate policy objects. The forwarding path selected the connection's ssl_context for wrapping the proxy socket instead of proxy_config.ssl_context, and it applied target verification flags to the shared proxy context. This maps to [CWE-295] Improper Certificate Validation.
Attack Vector
An attacker on the network path between the client and its HTTPS proxy presents an attacker-controlled certificate for the proxy hostname. Because the effective proxy policy no longer requires valid certificate verification, the client completes the TLS handshake with the attacker. The attacker then observes or modifies forwarded HTTP traffic and can receive any client certificate the application configured for the target.
# Fix in src/urllib3/connection.py (commit b6447295)
# Forwarding proxies should use proxy SSL context for
# wrapping since that's the connection being established,
# whereas tunneling proxies should use the connection's SSL
# context.
# However, for backwards compatibility reasons, if the proxy
# is forwarding but no proxy SSL context is provided, we
# fall back to using the connection's SSL context until
# urllib3 v3.0. Appropriate warning is emitted in
# ``ProxyManager.__init__``.
if self.proxy_is_forwarding and self.proxy_config is not None:
ssl_context = self.proxy_config.ssl_context
else:
ssl_context = self.ssl_context
sock_and_verified = _ssl_wrap_socket_and_match_hostname(
sock=sock,
cert_reqs=self.cert_reqs,
Source: urllib3 commit b6447295
Detection Methods for CVE-2026-97687
Indicators of Compromise
- Successful TLS handshakes to the configured HTTPS proxy using certificates not issued by the expected corporate or public CA chain.
- Unexpected client TLS certificate presentation to intermediate hosts that are not the configured HTTPS proxy.
- Application logs showing cert_reqs=CERT_NONE on a PoolManager or ProxyManager shared with an HTTPS proxy.
Detection Strategies
- Inventory Python dependencies to identify urllib3 versions between 1.26.0 and 2.7.x, including transitive installs via requests, boto3, and other clients.
- Audit source for use_forwarding_for_https=True, proxy_ssl_context, and per-request cert_reqs=CERT_NONE combinations against a shared pool.
- Compare TLS certificate fingerprints observed on proxy-bound connections against a known-good baseline for the proxy endpoint.
Monitoring Recommendations
- Enable TLS handshake logging on egress proxies and forward events to a centralized analytics pipeline for anomaly review.
- Alert on any change to proxy certificate issuer, subject, or fingerprint from client fleets.
- Track outbound connections from application hosts that terminate TLS at unexpected intermediate IPs.
How to Mitigate CVE-2026-97687
Immediate Actions Required
- Upgrade urllib3 to version 2.8.0 or later across all application and container images.
- Rebuild and redeploy any images that pin urllib3 or ship it via requests, botocore, or similar libraries.
- Rotate any TLS client certificates that may have been exposed to an untrusted proxy during the exposure window.
Patch Information
The fix is delivered in urllib3 2.8.0 via pull request #5093 and commits 07408cec and b6447295. The patch isolates proxy TLS settings, stops applying target SNI, assertions, and client credentials to forwarding proxy handshakes, and preserves a documented fallback to ssl_context when no proxy_ssl_context is provided. Full advisory: GHSA-8988-9cw3-xx77.
Workarounds
- Switch HTTPS proxies from forwarding mode to CONNECT tunneling, which preserves an independent end-to-end TLS session to the target.
- Provide an explicit proxy_ssl_context with verify_mode=CERT_REQUIRED and pinned CA bundle, and avoid sharing it with target requests that use CERT_NONE.
- Never set cert_reqs=CERT_NONE on a pool that also serves an HTTPS proxy connection.
# Upgrade urllib3 to the fixed release
pip install --upgrade 'urllib3>=2.8.0'
# Verify the installed version
python -c "import urllib3; print(urllib3.__version__)"
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

