Skip to content

verify_flags not propagated to inner _ctx; VERIFY_X509_STRICT silently dropped during handshake #211

@notatallshaw-gts

Description

@notatallshaw-gts

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions