You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
SSLContext.hostname_checks_common_name appears to have no effect #87688
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
assignee='https://github.com/tiran'closed_at=<Date2021-04-17.10:47:03.885>created_at=<Date2021-03-16.20:07:50.463>labels= ['expert-SSL', 'type-bug', '3.8', '3.9', '3.10']
title='SSLContext.hostname_checks_common_name appears to have no effect'updated_at=<Date2021-05-04.05:50:13.339>user='https://github.com/pquentin'
For this reason, we want to set hostname_checks_common_name to False on Python 3.7+ and OpenSSL 1.1.0+. (In other cases, we use a modified version of ssl.match_hostname that does not consider common names.)
I would expect that setting hostname_checks_common_name to False would rejects certificates without SANs, but that does not appear to be the case. I used the following Python code:
To reproduce this, I used trustme (https://trustme.readthedocs.io/en/latest/). I modified the code to not include a SAN at all and ran gunicorn --keyfile server.key --certfile server.pem app:app, with app being the Flask quickstart application. I'll try to attach all those files if I manage to do it.
Oh heck, this is a genuine bug. I'm not yet sure if it's an undocumented API quirk in OpenSSL, a design bug in OpenSSL, or a bug in my code.
Python sets the host flags on the X509_VERIFY_PARAM of the *SSL_CTX. All flags get copied to *SSL struct and later to *X509_STORE_CTX struct. At least I thought that all flags get copied. Apparently hostflags aren't copied from *SSL_CTX to *SSL because the *SSL_CTX doesn't have any verify hosts configured. They are only ever configured on *SSL struct.
Thank you for the quick fix! 🙏 Both the reproducer and the urllib3 test suite run fine with this change.
However, we can't trust HAS_NEVER_CHECK_COMMON_NAME anymore, because it will be True in Python versions where hostname_checks_common_name does not work. Is it possible to have, uh, REALLY_HAS_NEVER_CHECK_COMMON_NAME_I_PROMISE or something like that? :D It could even be private. Otherwise, we will only be able to use hostname_checks_common_name in Python 3.10.0a7+.
Seth's urllib3 newsletter reminded me that I forgot to link to OpenSSL issues here.
The problem was caused by a bug in OpenSSL. The issue is fixed in OpenSSL default branch and is scheduled to land in next 1.1.1 release. My changes to Python's ssl module are backports of my OpenSSL fixes for older 1.1.1 releases.
Activity
pquentin commentedon Mar 16, 2021
urllib3 is preparing a v2 with various SSL improvements, such as leaning on the ssl module to match hostnames when possible and reject certificates without a SAN. See https://urllib3.readthedocs.io/en/latest/v2-roadmap.html#modern-security-by-default for more details.
For this reason, we want to set
hostname_checks_common_nameto False on Python 3.7+ and OpenSSL 1.1.0+. (In other cases, we use a modified version ofssl.match_hostnamethat does not consider common names.)I would expect that setting
hostname_checks_common_nameto False would rejects certificates without SANs, but that does not appear to be the case. I used the following Python code:which prints
OpenSSL 1.1.1i 8 Dec 2020and does not fail as expected. I'm testing this on macOS 11.2.2 but this currently breaks our test suite on Ubuntu, Windows and macOS, including on Python 3.10, see https://github.com/urllib3/urllib3/runs/2122811894?check_suite_focus=true.To reproduce this, I used trustme (https://trustme.readthedocs.io/en/latest/). I modified the code to not include a SAN at all and ran
gunicorn --keyfile server.key --certfile server.pem app:app, with app being the Flask quickstart application. I'll try to attach all those files if I manage to do it.What am I missing?
tiran commentedon Mar 16, 2021
Oh heck, this is a genuine bug. I'm not yet sure if it's an undocumented API quirk in OpenSSL, a design bug in OpenSSL, or a bug in my code.
Python sets the host flags on the X509_VERIFY_PARAM of the *SSL_CTX. All flags get copied to *SSL struct and later to *X509_STORE_CTX struct. At least I thought that all flags get copied. Apparently hostflags aren't copied from *SSL_CTX to *SSL because the *SSL_CTX doesn't have any verify hosts configured. They are only ever configured on *SSL struct.
https://github.com/openssl/openssl/blob/081a7061f3da07318c4b0f5de67b82285630bf6b/crypto/x509/x509_vpm.c#L202-L213
tiran commentedon Mar 16, 2021
PS: I don't see any remark or warning about the behavior on the man pages https://www.openssl.org/docs/man1.1.1/man3/X509_VERIFY_PARAM_set_flags.html and https://www.openssl.org/docs/man1.1.1/man3/X509_check_host.html
pquentin commentedon Mar 17, 2021
Thank you for the quick fix! 🙏 Both the reproducer and the urllib3 test suite run fine with this change.
However, we can't trust
HAS_NEVER_CHECK_COMMON_NAMEanymore, because it will be True in Python versions wherehostname_checks_common_namedoes not work. Is it possible to have, uh,REALLY_HAS_NEVER_CHECK_COMMON_NAME_I_PROMISEor something like that? :D It could even be private. Otherwise, we will only be able to usehostname_checks_common_namein Python 3.10.0a7+.tiran commentedon Apr 17, 2021
New changeset b467d9a by Christian Heimes in branch 'master':
bpo-43522: Fix SSLContext.hostname_checks_common_name (GH-24899)
b467d9a
tiran commentedon Apr 17, 2021
New changeset cdf0287 by Christian Heimes in branch '3.9':
[3.9] bpo-43522: Fix SSLContext.hostname_checks_common_name (GH-24899) (GH-25451)
cdf0287
tiran commentedon Apr 17, 2021
New changeset f77ca86 by Christian Heimes in branch '3.8':
[3.8] bpo-43522: Fix SSLContext.hostname_checks_common_name (GH-24899) (GH-25452)
f77ca86
tiran commentedon Apr 17, 2021
Workaround has been added to upcoming 3.8 to 3.10 releases. Older versions will get fixed by next OpenSSL update.
2 remaining items
tiran commentedon May 4, 2021
Seth's urllib3 newsletter reminded me that I forgot to link to OpenSSL issues here.
The problem was caused by a bug in OpenSSL. The issue is fixed in OpenSSL default branch and is scheduled to land in next 1.1.1 release. My changes to Python's ssl module are backports of my OpenSSL fixes for older 1.1.1 releases.
openssl/openssl#14579
openssl/openssl#14856
openssl/openssl@dfccfde
gh-87688: Amend SSLContext.hostname_checks_common_name docs (GH-100517)
pythongh-87688: Amend SSLContext.hostname_checks_common_name docs (py…
pythongh-87688: Amend SSLContext.hostname_checks_common_name docs (py…
[3.11] gh-87688: Amend SSLContext.hostname_checks_common_name docs (G…
[3.12] gh-87688: Amend SSLContext.hostname_checks_common_name docs (G…
pythongh-87688: Amend SSLContext.hostname_checks_common_name docs (py…
pythongh-87688: Amend SSLContext.hostname_checks_common_name docs (py…
pythongh-87688: Amend SSLContext.hostname_checks_common_name docs (py…