From 6d61b8216d17b0bdf0569f8dd4555810bb80bd83 Mon Sep 17 00:00:00 2001 From: Brett Smith Date: Fri, 6 Nov 2026 11:50:41 -0500 Subject: [PATCH 1/3] GH-161503: Check hostname in ssl.get_server_certificate with custom ca_certs (#161503) --- Lib/ssl.py | 1 + Lib/test/test_ssl.py | 8 ++++++-- .../2026-11-06-16-22-47.gh-issue-161503.FkIl87.rst | 4 ++++ 3 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Security/2026-11-06-16-22-47.gh-issue-161503.FkIl87.rst diff --git a/Lib/ssl.py b/Lib/ssl.py index df2eece13f2b138..7666c5148b458fa 100644 --- a/Lib/ssl.py +++ b/Lib/ssl.py @@ -1650,6 +1650,7 @@ def get_server_certificate(addr, ssl_version=PROTOCOL_TLS_CLIENT, cert_reqs = CERT_NONE context = _create_stdlib_context(ssl_version, cert_reqs=cert_reqs, + check_hostname=ca_certs is not None, cafile=ca_certs) with create_connection(addr, timeout=timeout) as sock: with context.wrap_socket(sock, server_hostname=host) as sslsock: diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index 5b4e4f080607726..b0e3cbaa548e8d5 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -3112,6 +3112,10 @@ def test_get_server_certificate_fail(self): # independent test method _test_get_server_certificate_fail(self, *self.server_addr) + def test_get_server_certificate_fail_by_hostname(self): + _, port = self.server_addr + _test_get_server_certificate_fail(self, socket_helper.HOSTv4, port, SIGNING_CA) + def test_get_server_certificate_timeout(self): def servername_cb(ssl_sock, server_name, initial_context): time.sleep(0.2) @@ -3364,10 +3368,10 @@ def _test_get_server_certificate(test, host, port, cert=None): if support.verbose: sys.stdout.write("\nVerified certificate for %s:%s is\n%s\n" % (host, port ,pem)) -def _test_get_server_certificate_fail(test, host, port): +def _test_get_server_certificate_fail(test, host, port, ca_certs=CERTFILE): with warnings_helper.check_no_resource_warning(test): try: - pem = ssl.get_server_certificate((host, port), ca_certs=CERTFILE) + pem = ssl.get_server_certificate((host, port), ca_certs=ca_certs) except ssl.SSLError as x: #should fail if support.verbose: diff --git a/Misc/NEWS.d/next/Security/2026-11-06-16-22-47.gh-issue-161503.FkIl87.rst b/Misc/NEWS.d/next/Security/2026-11-06-16-22-47.gh-issue-161503.FkIl87.rst new file mode 100644 index 000000000000000..9c92ff1f21359be --- /dev/null +++ b/Misc/NEWS.d/next/Security/2026-11-06-16-22-47.gh-issue-161503.FkIl87.rst @@ -0,0 +1,4 @@ +.. cve: CVE-2026-19552 + +:func:`ssl.get_server_certificate` verifies that the server hostname matches +its certificate when the caller specifies ``ca_certs``. From 2530d72f43e8efc18090be912634b4a14e9c124c Mon Sep 17 00:00:00 2001 From: Brett Smith Date: Fri, 6 Nov 2026 12:34:14 -0500 Subject: [PATCH 2/3] GH-161503: Update documentation for the new check --- Doc/library/ssl.rst | 8 +++++++- Lib/ssl.py | 3 ++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst index 139dc2c24eab764..23aba314ac5b386 100644 --- a/Doc/library/ssl.rst +++ b/Doc/library/ssl.rst @@ -432,7 +432,8 @@ Certificate handling same format as used for the *cafile* parameter in :meth:`SSLContext.load_verify_locations`. The call will attempt to validate the server certificate against that set of root certificates, and will fail - if the validation attempt fails. A timeout can be specified with the + if the validation attempt fails or the certificate hostname does not match + the server hostname. A timeout can be specified with the ``timeout`` parameter. .. versionchanged:: 3.3 @@ -445,6 +446,11 @@ Certificate handling .. versionchanged:: 3.10 The *timeout* parameter was added. + .. versionchanged:: 3.16 + The function now raises :exc:`SSLCertVerificationError` if *ca_certs* is + specified but the certificate hostname does not match the server + hostname. + .. function:: DER_cert_to_PEM_cert(der_cert_bytes) Given a certificate as a DER-encoded blob of bytes, returns a PEM-encoded diff --git a/Lib/ssl.py b/Lib/ssl.py index 7666c5148b458fa..49eec9cde0b6317 100644 --- a/Lib/ssl.py +++ b/Lib/ssl.py @@ -1638,7 +1638,8 @@ def get_server_certificate(addr, ssl_version=PROTOCOL_TLS_CLIENT, ca_certs=None, timeout=_GLOBAL_DEFAULT_TIMEOUT): """Retrieve the certificate from the server at the specified address, and return it as a PEM-encoded string. - If 'ca_certs' is specified, validate the server cert against it. + If 'ca_certs' is specified, validate the server cert against it and + check that it matches the hostname. If 'ssl_version' is specified, use it in the connection attempt. If 'timeout' is specified, use it in the connection attempt. """ From e05d00345f8c44defc2093581e1a1e88b0bcfef4 Mon Sep 17 00:00:00 2001 From: Brett Smith Date: Fri, 6 Nov 2026 15:31:57 -0500 Subject: [PATCH 3/3] GH-161503: Refine documentation per review --- Doc/library/ssl.rst | 2 +- .../Security/2026-11-06-16-22-47.gh-issue-161503.FkIl87.rst | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst index 23aba314ac5b386..50409c89929f01b 100644 --- a/Doc/library/ssl.rst +++ b/Doc/library/ssl.rst @@ -446,7 +446,7 @@ Certificate handling .. versionchanged:: 3.10 The *timeout* parameter was added. - .. versionchanged:: 3.16 + .. versionchanged:: next The function now raises :exc:`SSLCertVerificationError` if *ca_certs* is specified but the certificate hostname does not match the server hostname. diff --git a/Misc/NEWS.d/next/Security/2026-11-06-16-22-47.gh-issue-161503.FkIl87.rst b/Misc/NEWS.d/next/Security/2026-11-06-16-22-47.gh-issue-161503.FkIl87.rst index 9c92ff1f21359be..308ba0c83f3efe4 100644 --- a/Misc/NEWS.d/next/Security/2026-11-06-16-22-47.gh-issue-161503.FkIl87.rst +++ b/Misc/NEWS.d/next/Security/2026-11-06-16-22-47.gh-issue-161503.FkIl87.rst @@ -1,4 +1,4 @@ .. cve: CVE-2026-19552 -:func:`ssl.get_server_certificate` verifies that the server hostname matches -its certificate when the caller specifies ``ca_certs``. +:func:`ssl.get_server_certificate` now verifies that the server hostname matches +its certificate when the caller specifies *ca_certs*.