Repository navigation
ssl.create_default_context(): add VERIFY_X509_STRICT and VERIFY_X509_PARTIAL_CHAIN to the default verify_flags #107361
Description
Feature or enhancement
My proposal is to add two new flags to the SSLContext.verify_flags created within ssl.create_default_context():
VERIFY_X509_STRICT: This will enable stricter RFC 5280 compliance checks within OpenSSL's X.509 path validation implementation. This will have no effect on the overwhelming majority of users (since the overwhelming majority of TLS connections are likely going through the Web PKI, which is already much stricter than RFC 5280).VERIFY_X509_PARTIAL_CHAIN: This will allow OpenSSL's path validation to terminate at the first certificate listed in the user's root of trust, even if that certificate is not self-signed. Despite the confusing name and the fact that this isn't the default, this is the correct behavior per the various X.509 RFCs (3280, 5280): a trust anchor is defined to be an a priori trust relationship with a subject and its public key, regardless of whether the anchor comes in certificate form or is signed by another member of the trusted store. This should have no breaking effect on any users, but may cause some validations to produce shorter chains than the currentSSLContextenables.
My proposal is consistent with the stability policy for create_default_context, which says that CPython may introduce changes to the default context without a prior deprecation period:
The protocol, options, cipher and other settings may change to more restrictive values anytime without prior deprecation. The values represent a fair balance between compatibility and security.
As with previous changes to the default context (such as the removal of 3DES support with 3.6), these proposed changes will not prevent people from constructing their own SSLContext without these new flags. In other words: users who do experience breakage or other behavioral changes will have a well-trodden alternative available to them, one that is documented as a matter of policy.
Pitch
To summarize from DPO:
- Enabling
VERIFY_X509_STRICTis a net security win: it reduces the amount of flexibility in the X.509 certificates that thesslmodule accepts by default, which means less attacker controlled flexibility. It also makes CPython itself more compatible with the X.509 profile defined in RFC 5280, meaning that end-users can more confidently expect interoperation with PKIs that use the 5280 profile. - Enabling
VERIFY_X509_PARTIAL_CHAINmakessslcomply more closely with other path validation/building implementations (like Go's), and eliminates an error mode that's only possible because of OpenSSL's non-standard default behavior (a chain is built to the root of trust successfully, but can still fail because OpenSSL can't find a self-signed root also within the root of trust). It's also consistent with what curl and other downstream consumers of OpenSSL do.
Previous discussion
See DPO discussion here: https://discuss.python.org/t/ssl-changing-the-default-sslcontext-verify-flags/30230
Activity
alex commentedon Jul 27, 2023
@python/crypto-team
pythongh-107361: strengthen default SSL context flags
woodruffw commentedon Nov 25, 2023
Opened #112389 for this.
gh-107361: strengthen default SSL context flags (#112389)
gpshead commentedon Mar 6, 2024
Thanks for taking the time to do this!
pythongh-107361: strengthen default SSL context flags (python#112389)
pythongh-107361: strengthen default SSL context flags (python#112389)
tests: fix SSL test on python 3.13 by being more lenient about VERIFY…
7 remaining items
tests: generate test certificates that pass python 3.13 criteria
tests: generate test certificates that pass python 3.13 criteria
tests: generate test certificates that pass python 3.13 criteria
tests: generate test certificates that pass python 3.13 criteria
tests: generate test certificates that pass python 3.13 criteria
Tasssadar commentedon May 22, 2025
Hello, the
VERIFY_X509_STRICTis breaking connections on some SSL inspection proxies, which replace the original SSL cert, withMissing Authority Key Identifier. I've tried to check some docs, and I'm not even sure they can generate a "fake" certificate with valid AKI actually.No browsers seem to require this either, so openssl seems stricter here.
I'm not trying to say this is a wrong change or that you should not do it, just trying to provide some feedback, not sure if you get much of it after changes like these.
Also, looking at the discussion, there were some examples mentioned of other projects making SSL stricter, and introducing env variables to disable this stricter mode. AFAIK golang project also does this, and I'd like to propose you to adopt the same approach with future changes like this - setting env variable is significantly easier than hunting down every place where ssl context is created, and setting some flag to it.
woodruffw commentedon May 22, 2025
This is possible: the AKI is tied to whatever the SSL inspection proxy's root cert is. For context, I believe another large SSL proxy vendor has already fixed this in their products based on the Python change here. I'll ping some people and see if they're willing to share more information about that.
There's some nuance here: modern browsers are aware of inspection proxies, and apply looser validation rules to them because they're persistently misconfigured (e.g. violating RFC 5280 in this case). Chrome and Firefox should both refuse a similarly mis-issued certificate on the Web PKI, although this is difficult to provide a public example of since Web PKI CAs will also refuse to issue an invalid certificate here.
This is ultimately the maintainers' call, but I would be a -1 on it as an outsider: IMO environment variables are too easy to "set and forget," leaving deployments in a permanently degraded security posture. This is the point I made in this DPO comment.
(I believe the official advice with Python and the
sslmodule is thatcreate_default_contextcan change at any time for security reasons. If you're in an environment where stability is more important than a default security posture, the docs recommend creating your ownSSLContext. However, per above, this is something that your SSL proxy vendor should fix directly -- they're currently violating Web PKI requirements that the entire public Internet has adopted 🙂)Tasssadar commentedon May 22, 2025
Thank you for this information, it is very hard to come by, so this is much appreciated!
Thank you, we'll try to talk to our vendor - if you'd be able to get any more information, I'd be very grateful.
I understand your reasoning, and I'd love this to have the wanted effect, but the reality is the SSL proxy will not be fixed anytime soon even if it is entirely at fault, so I now have two options - either not use Python 3.13, or disable the extra validation. And without env var, the "easy to set and forget" thing is now everywhere an ssl context is created instead of, say, one base Docker image.
klausenbusk commentedon May 28, 2025
VERIFY_X509_STRICTapparently requires the optional (from a CA baseline perspective) "Subject Key Identifier" to be set. That is not the case for all CAs included in Mozilla Root Program, in particular it is missing for "TWCA Global Root CA".So all leaf certificates with trust to said CA now result in a
ssl.SSLCertVerificationError(for example the CA own website: https://www.twca.com.tw). I don't use said CA myself and only noticed due to a user report.Is this expected?
woodruffw commentedon May 29, 2025
The nuance here is that CABF 7.1.2.11.4 points to RFC 5280 4.2.1.2, which says:
Given that, my interpretation (which could be wrong) of CABF's language here is that "If present" is intended to provide leeway for existing root program members, not act as normative guidance for new CA issuances. In other words, a CA issued in 2025 would be considered mis-issued if it was missing an SKI, but older CAs that don't conform to RFC 5280 are seemingly permitted in this instance by CABF.
Yes and no 😅 -- "yes" in the sense that this change intentionally enforces strict RFC 5280 conformance, and "no" in the sense that the intention wasn't to break chains actually present in the Web PKI (versus ratcheting down on chains in private PKIs).
However, I'm actually a bit confused as to how this is breaking: my connection to
www.twca.com.twshowsTWCA Global EVSSL Certification Authorityas the ICA, which in turn has at least one issued CA certificate with a conforming SKI extension as well as an issued root CA certificate with a conforming SKI. So I suspect that what's happening here is that the CA has issued conforming certs, but that not all root programs have adopted them. I'll need to look into that more.Edit: for context:
www.twca.com.twthat I get: https://crt.sh/?id=16948166955TWCA Global Root CAandTWCA Root Certification Authorityshould be in the root programTWCA Root Certification Authorityhas a correct-looking SKI: https://crt.sh/?id=1566986Edit 2: the plot thickens, as my macOS machine's root program does contain the expected certificate for
TWCA Root Certification Authority:...and so does
/private/etc/ssl/cert.pemforTWCA Root Certification Authoritybut notTWCA Global Root CA:That version of
TWCA Global Root CAappears to be https://crt.sh/?id=8559119, which PKI linters do indeed complain about: https://crt.sh/?id=8559119&opt=pkimetalEdit 3:
Looks like the keychain/PEM bundle thing was a red herring, since the keychain version of
TWCA Global Root CAis also missing an SKI:So, recap:
TWCA Global Root CA, and is https://crt.sh/?id=8559119.Support ssl.VERIFY_X509_STRICT.
tests: Regenerate DNF server certs for Python 3.13
tests: Regenerate DNF server certs for Python 3.13
chore(backend): migrate to python 3.14 (#2573)
interface: disable ssl.VERIFY_X509_STRICT for self-signed certs