-
Notifications
You must be signed in to change notification settings - Fork 25
verify_flags not propagated to inner _ctx; VERIFY_X509_STRICT silently dropped during handshake #211
Description
I was debugging an issue at work where on a new machine, in a conda environment that has Python 3.13+ and OpenSSL 3.x I was getting an error from conda:
Collecting package metadata (repodata.json): - Retrying (Retry(total=2, connect=None, read=None, redirect=None, status=None)) after connection broken by 'SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: Missing Authority Key Identifier (_ssl.c:1032)'))': /conda-forge/noarch/repodata.json.zst
But pip 26.0, that uses truststore was working just fine. I figured out that Python 3.13 added VERIFY_X509_STRICT to ssl.create_default_context() (python/cpython#107361).
However, my understanding from reading the code is, that truststore.SSLContext creates its inner _ctx via SSLContext(PROTOCOL_TLS_CLIENT), which does not include STRICT:
ssl.create_default_context().verify_flags = 557088 (STRICT | PARTIAL_CHAIN | TRUSTED_FIRST)
ssl.SSLContext(PROTOCOL_TLS_CLIENT).verify_flags = 32768 (TRUSTED_FIRST only)
The verify_flags setter in _api.py writes to the wrapper via _original_super_SSLContext.verify_mode.set(self, value), not to self._ctx. So when callers (e.g. urllib3) set VERIFY_X509_STRICT on the truststore SSLContext, the flag lands on the wrapper and never reaches the inner _ctx that performs the actual TLS handshake.
This meant that my corporate TLS-intercepting CA cert that is missing the Authority Key Identifier extension is rejected by ssl.create_default_context(), but accepted by truststore because the inner _ctx never receives the STRICT flag.
Code I was looking at to verify this:
from truststore import SSLContext
import ssl
ctx = SSLContext(ssl.PROTOCOL_TLS_CLIENT)
ctx.verify_flags |= ssl.VERIFY_X509_STRICT
print(f'wrapper verify_flags={ctx.verify_flags}')
print(f'inner _ctx verify_flags={ctx._ctx.verify_flags}')Expected: both should include STRICT (32).
Actual: inner _ctx does not.
I'm really out of my depth here, so I could be wrong about all of this.