mirror of
https://github.com/python/cpython.git
synced 2025-08-23 18:24:46 +00:00
merge 3.4 (#22417)
This commit is contained in:
commit
1cca273669
11 changed files with 115 additions and 69 deletions
|
@ -71,12 +71,6 @@ The module provides the following classes:
|
||||||
:func:`ssl.create_default_context` select the system's trusted CA
|
:func:`ssl.create_default_context` select the system's trusted CA
|
||||||
certificates for you.
|
certificates for you.
|
||||||
|
|
||||||
The recommended way to connect to HTTPS hosts on the Internet is as
|
|
||||||
follows::
|
|
||||||
|
|
||||||
context = ssl.create_default_context()
|
|
||||||
h = client.HTTPSConnection('www.python.org', 443, context=context)
|
|
||||||
|
|
||||||
Please read :ref:`ssl-security` for more information on best practices.
|
Please read :ref:`ssl-security` for more information on best practices.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
@ -97,6 +91,12 @@ The module provides the following classes:
|
||||||
The *strict* parameter was removed. HTTP 0.9-style "Simple Responses" are
|
The *strict* parameter was removed. HTTP 0.9-style "Simple Responses" are
|
||||||
no longer supported.
|
no longer supported.
|
||||||
|
|
||||||
|
.. versionchanged:: 3.4.3
|
||||||
|
This class now performs all the necessary certificate and hostname checks
|
||||||
|
by default. To revert to the previous, unverified, behavior
|
||||||
|
:func:`ssl._create_unverified_context` can be passed to the *context*
|
||||||
|
parameter.
|
||||||
|
|
||||||
|
|
||||||
.. class:: HTTPResponse(sock, debuglevel=0, method=None, url=None)
|
.. class:: HTTPResponse(sock, debuglevel=0, method=None, url=None)
|
||||||
|
|
||||||
|
|
|
@ -62,11 +62,6 @@ The :mod:`urllib.request` module defines the following functions:
|
||||||
*cafile* and *capath* parameters are omitted. This will only work on
|
*cafile* and *capath* parameters are omitted. This will only work on
|
||||||
some non-Windows platforms.
|
some non-Windows platforms.
|
||||||
|
|
||||||
.. warning::
|
|
||||||
If neither *cafile* nor *capath* is specified, and *cadefault* is ``False``,
|
|
||||||
an HTTPS request will not do any verification of the server's
|
|
||||||
certificate.
|
|
||||||
|
|
||||||
For http and https urls, this function returns a
|
For http and https urls, this function returns a
|
||||||
:class:`http.client.HTTPResponse` object which has the following
|
:class:`http.client.HTTPResponse` object which has the following
|
||||||
:ref:`httpresponse-objects` methods.
|
:ref:`httpresponse-objects` methods.
|
||||||
|
|
|
@ -27,11 +27,10 @@ between conformable Python objects and XML on the wire.
|
||||||
constructed data. If you need to parse untrusted or unauthenticated data see
|
constructed data. If you need to parse untrusted or unauthenticated data see
|
||||||
:ref:`xml-vulnerabilities`.
|
:ref:`xml-vulnerabilities`.
|
||||||
|
|
||||||
.. warning::
|
.. versionchanged:: 3.4.3
|
||||||
|
|
||||||
In the case of https URIs, :mod:`xmlrpc.client` does not do any verification
|
|
||||||
of the server's certificate.
|
|
||||||
|
|
||||||
|
For https URIs, :mod:`xmlrpc.client` now performs all the necessary
|
||||||
|
certificate and hostname checks by default
|
||||||
|
|
||||||
.. class:: ServerProxy(uri, transport=None, encoding=None, verbose=False, \
|
.. class:: ServerProxy(uri, transport=None, encoding=None, verbose=False, \
|
||||||
allow_none=False, use_datetime=False, \
|
allow_none=False, use_datetime=False, \
|
||||||
|
|
|
@ -2504,3 +2504,32 @@ Changes in the C API
|
||||||
* The ``f_tstate`` (thread state) field of the :c:type:`PyFrameObject`
|
* The ``f_tstate`` (thread state) field of the :c:type:`PyFrameObject`
|
||||||
structure has been removed to fix a bug: see :issue:`14432` for the
|
structure has been removed to fix a bug: see :issue:`14432` for the
|
||||||
rationale.
|
rationale.
|
||||||
|
|
||||||
|
Changed in 3.4.3
|
||||||
|
================
|
||||||
|
|
||||||
|
.. _pep-476:
|
||||||
|
|
||||||
|
PEP 476: Enabling certificate verification by default for stdlib http clients
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
:mod:`http.client` and modules which use it, such as :mod:`urllib.request` and
|
||||||
|
:mod:`xmlrpc.client`, will now verify that the server presents a certificate
|
||||||
|
which is signed by a CA in the platform trust store and whose hostname matches
|
||||||
|
the hostname being requested by default, significantly improving security for
|
||||||
|
many applications.
|
||||||
|
|
||||||
|
For applications which require the old previous behavior, they can pass an
|
||||||
|
alternate context::
|
||||||
|
|
||||||
|
import urllib.request
|
||||||
|
import ssl
|
||||||
|
|
||||||
|
# This disables all verification
|
||||||
|
context = ssl._create_unverified_context()
|
||||||
|
|
||||||
|
# This allows using a specific certificate for the host, which doesn't need
|
||||||
|
# to be in the trust store
|
||||||
|
context = ssl.create_default_context(cafile="/path/to/file.crt")
|
||||||
|
|
||||||
|
urllib.request.urlopen("https://invalid-cert", context=context)
|
||||||
|
|
|
@ -1267,7 +1267,7 @@ else:
|
||||||
self.key_file = key_file
|
self.key_file = key_file
|
||||||
self.cert_file = cert_file
|
self.cert_file = cert_file
|
||||||
if context is None:
|
if context is None:
|
||||||
context = ssl._create_stdlib_context()
|
context = ssl._create_default_https_context()
|
||||||
will_verify = context.verify_mode != ssl.CERT_NONE
|
will_verify = context.verify_mode != ssl.CERT_NONE
|
||||||
if check_hostname is None:
|
if check_hostname is None:
|
||||||
check_hostname = will_verify
|
check_hostname = will_verify
|
||||||
|
|
10
Lib/ssl.py
10
Lib/ssl.py
|
@ -436,8 +436,7 @@ def create_default_context(purpose=Purpose.SERVER_AUTH, *, cafile=None,
|
||||||
context.load_default_certs(purpose)
|
context.load_default_certs(purpose)
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
def _create_unverified_context(protocol=PROTOCOL_SSLv23, *, cert_reqs=None,
|
||||||
def _create_stdlib_context(protocol=PROTOCOL_SSLv23, *, cert_reqs=None,
|
|
||||||
check_hostname=False, purpose=Purpose.SERVER_AUTH,
|
check_hostname=False, purpose=Purpose.SERVER_AUTH,
|
||||||
certfile=None, keyfile=None,
|
certfile=None, keyfile=None,
|
||||||
cafile=None, capath=None, cadata=None):
|
cafile=None, capath=None, cadata=None):
|
||||||
|
@ -478,6 +477,13 @@ def _create_stdlib_context(protocol=PROTOCOL_SSLv23, *, cert_reqs=None,
|
||||||
|
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
# Used by http.client if no context is explicitly passed.
|
||||||
|
_create_default_https_context = create_default_context
|
||||||
|
|
||||||
|
|
||||||
|
# Backwards compatibility alias, even though it's not a public name.
|
||||||
|
_create_stdlib_context = _create_unverified_context
|
||||||
|
|
||||||
|
|
||||||
class SSLObject:
|
class SSLObject:
|
||||||
"""This class implements an interface on top of a low-level SSL object as
|
"""This class implements an interface on top of a low-level SSL object as
|
||||||
|
|
|
@ -1012,13 +1012,36 @@ class HTTPSTest(TestCase):
|
||||||
self.assertIn('Apache', server_string)
|
self.assertIn('Apache', server_string)
|
||||||
|
|
||||||
def test_networked(self):
|
def test_networked(self):
|
||||||
# Default settings: no cert verification is done
|
# Default settings: requires a valid cert from a trusted CA
|
||||||
|
import ssl
|
||||||
support.requires('network')
|
support.requires('network')
|
||||||
with support.transient_internet('svn.python.org'):
|
with support.transient_internet('self-signed.pythontest.net'):
|
||||||
h = client.HTTPSConnection('svn.python.org', 443)
|
h = client.HTTPSConnection('self-signed.pythontest.net', 443)
|
||||||
|
with self.assertRaises(ssl.SSLError) as exc_info:
|
||||||
|
h.request('GET', '/')
|
||||||
|
self.assertEqual(exc_info.exception.reason, 'CERTIFICATE_VERIFY_FAILED')
|
||||||
|
|
||||||
|
def test_networked_noverification(self):
|
||||||
|
# Switch off cert verification
|
||||||
|
import ssl
|
||||||
|
support.requires('network')
|
||||||
|
with support.transient_internet('self-signed.pythontest.net'):
|
||||||
|
context = ssl._create_unverified_context()
|
||||||
|
h = client.HTTPSConnection('self-signed.pythontest.net', 443,
|
||||||
|
context=context)
|
||||||
h.request('GET', '/')
|
h.request('GET', '/')
|
||||||
resp = h.getresponse()
|
resp = h.getresponse()
|
||||||
self._check_svn_python_org(resp)
|
self.assertIn('nginx', resp.getheader('server'))
|
||||||
|
|
||||||
|
def test_networked_trusted_by_default_cert(self):
|
||||||
|
# Default settings: requires a valid cert from a trusted CA
|
||||||
|
support.requires('network')
|
||||||
|
with support.transient_internet('www.python.org'):
|
||||||
|
h = client.HTTPSConnection('www.python.org', 443)
|
||||||
|
h.request('GET', '/')
|
||||||
|
resp = h.getresponse()
|
||||||
|
content_type = resp.getheader('content-type')
|
||||||
|
self.assertIn('text/html', content_type)
|
||||||
|
|
||||||
def test_networked_good_cert(self):
|
def test_networked_good_cert(self):
|
||||||
# We feed a CA cert that validates the server's cert
|
# We feed a CA cert that validates the server's cert
|
||||||
|
@ -1037,13 +1060,23 @@ class HTTPSTest(TestCase):
|
||||||
# We feed a "CA" cert that is unrelated to the server's cert
|
# We feed a "CA" cert that is unrelated to the server's cert
|
||||||
import ssl
|
import ssl
|
||||||
support.requires('network')
|
support.requires('network')
|
||||||
with support.transient_internet('svn.python.org'):
|
with support.transient_internet('self-signed.pythontest.net'):
|
||||||
context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
|
context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
|
||||||
context.verify_mode = ssl.CERT_REQUIRED
|
context.verify_mode = ssl.CERT_REQUIRED
|
||||||
context.load_verify_locations(CERT_localhost)
|
context.load_verify_locations(CERT_localhost)
|
||||||
h = client.HTTPSConnection('svn.python.org', 443, context=context)
|
h = client.HTTPSConnection('self-signed.pythontest.net', 443, context=context)
|
||||||
with self.assertRaises(ssl.SSLError):
|
with self.assertRaises(ssl.SSLError) as exc_info:
|
||||||
h.request('GET', '/')
|
h.request('GET', '/')
|
||||||
|
self.assertEqual(exc_info.exception.reason, 'CERTIFICATE_VERIFY_FAILED')
|
||||||
|
|
||||||
|
def test_local_unknown_cert(self):
|
||||||
|
# The custom cert isn't known to the default trust bundle
|
||||||
|
import ssl
|
||||||
|
server = self.make_server(CERT_localhost)
|
||||||
|
h = client.HTTPSConnection('localhost', server.port)
|
||||||
|
with self.assertRaises(ssl.SSLError) as exc_info:
|
||||||
|
h.request('GET', '/')
|
||||||
|
self.assertEqual(exc_info.exception.reason, 'CERTIFICATE_VERIFY_FAILED')
|
||||||
|
|
||||||
def test_local_good_hostname(self):
|
def test_local_good_hostname(self):
|
||||||
# The (valid) cert validates the HTTP hostname
|
# The (valid) cert validates the HTTP hostname
|
||||||
|
@ -1056,7 +1089,6 @@ class HTTPSTest(TestCase):
|
||||||
h.request('GET', '/nonexistent')
|
h.request('GET', '/nonexistent')
|
||||||
resp = h.getresponse()
|
resp = h.getresponse()
|
||||||
self.assertEqual(resp.status, 404)
|
self.assertEqual(resp.status, 404)
|
||||||
del server
|
|
||||||
|
|
||||||
def test_local_bad_hostname(self):
|
def test_local_bad_hostname(self):
|
||||||
# The (valid) cert doesn't validate the HTTP hostname
|
# The (valid) cert doesn't validate the HTTP hostname
|
||||||
|
@ -1079,7 +1111,6 @@ class HTTPSTest(TestCase):
|
||||||
h.request('GET', '/nonexistent')
|
h.request('GET', '/nonexistent')
|
||||||
resp = h.getresponse()
|
resp = h.getresponse()
|
||||||
self.assertEqual(resp.status, 404)
|
self.assertEqual(resp.status, 404)
|
||||||
del server
|
|
||||||
|
|
||||||
@unittest.skipIf(not hasattr(client, 'HTTPSConnection'),
|
@unittest.skipIf(not hasattr(client, 'HTTPSConnection'),
|
||||||
'http.client.HTTPSConnection not available')
|
'http.client.HTTPSConnection not available')
|
||||||
|
|
|
@ -1627,36 +1627,6 @@ class UnixSysLogHandlerTest(SysLogHandlerTest):
|
||||||
class HTTPHandlerTest(BaseTest):
|
class HTTPHandlerTest(BaseTest):
|
||||||
"""Test for HTTPHandler."""
|
"""Test for HTTPHandler."""
|
||||||
|
|
||||||
PEMFILE = """-----BEGIN RSA PRIVATE KEY-----
|
|
||||||
MIICXQIBAAKBgQDGT4xS5r91rbLJQK2nUDenBhBG6qFk+bVOjuAGC/LSHlAoBnvG
|
|
||||||
zQG3agOG+e7c5z2XT8m2ktORLqG3E4mYmbxgyhDrzP6ei2Anc+pszmnxPoK3Puh5
|
|
||||||
aXV+XKt0bU0C1m2+ACmGGJ0t3P408art82nOxBw8ZHgIg9Dtp6xIUCyOqwIDAQAB
|
|
||||||
AoGBAJFTnFboaKh5eUrIzjmNrKsG44jEyy+vWvHN/FgSC4l103HxhmWiuL5Lv3f7
|
|
||||||
0tMp1tX7D6xvHwIG9VWvyKb/Cq9rJsDibmDVIOslnOWeQhG+XwJyitR0pq/KlJIB
|
|
||||||
5LjORcBw795oKWOAi6RcOb1ON59tysEFYhAGQO9k6VL621gRAkEA/Gb+YXULLpbs
|
|
||||||
piXN3q4zcHzeaVANo69tUZ6TjaQqMeTxE4tOYM0G0ZoSeHEdaP59AOZGKXXNGSQy
|
|
||||||
2z/MddcYGQJBAMkjLSYIpOLJY11ja8OwwswFG2hEzHe0cS9bzo++R/jc1bHA5R0Y
|
|
||||||
i6vA5iPi+wopPFvpytdBol7UuEBe5xZrxWMCQQCWxELRHiP2yWpEeLJ3gGDzoXMN
|
|
||||||
PydWjhRju7Bx3AzkTtf+D6lawz1+eGTuEss5i0JKBkMEwvwnN2s1ce+EuF4JAkBb
|
|
||||||
E96h1lAzkVW5OAfYOPY8RCPA90ZO/hoyg7PpSxR0ECuDrgERR8gXIeYUYfejBkEa
|
|
||||||
rab4CfRoVJKKM28Yq/xZAkBvuq670JRCwOgfUTdww7WpdOQBYPkzQccsKNCslQW8
|
|
||||||
/DyW6y06oQusSENUvynT6dr3LJxt/NgZPhZX2+k1eYDV
|
|
||||||
-----END RSA PRIVATE KEY-----
|
|
||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIICGzCCAYSgAwIBAgIJAIq84a2Q/OvlMA0GCSqGSIb3DQEBBQUAMBQxEjAQBgNV
|
|
||||||
BAMTCWxvY2FsaG9zdDAeFw0xMTA1MjExMDIzMzNaFw03NTAzMjEwMzU1MTdaMBQx
|
|
||||||
EjAQBgNVBAMTCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA
|
|
||||||
xk+MUua/da2yyUCtp1A3pwYQRuqhZPm1To7gBgvy0h5QKAZ7xs0Bt2oDhvnu3Oc9
|
|
||||||
l0/JtpLTkS6htxOJmJm8YMoQ68z+notgJ3PqbM5p8T6Ctz7oeWl1flyrdG1NAtZt
|
|
||||||
vgAphhidLdz+NPGq7fNpzsQcPGR4CIPQ7aesSFAsjqsCAwEAAaN1MHMwHQYDVR0O
|
|
||||||
BBYEFLWaUPO6N7efGiuoS9i3DVYcUwn0MEQGA1UdIwQ9MDuAFLWaUPO6N7efGiuo
|
|
||||||
S9i3DVYcUwn0oRikFjAUMRIwEAYDVQQDEwlsb2NhbGhvc3SCCQCKvOGtkPzr5TAM
|
|
||||||
BgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAMK5whPjLNQK1Ivvk88oqJqq
|
|
||||||
4f889OwikGP0eUhOBhbFlsZs+jq5YZC2UzHz+evzKBlgAP1u4lP/cB85CnjvWqM+
|
|
||||||
1c/lywFHQ6HOdDeQ1L72tSYMrNOG4XNmLn0h7rx6GoTU7dcFRfseahBCq8mv0IDt
|
|
||||||
IRbTpvlHWPjsSvHz0ZOH
|
|
||||||
-----END CERTIFICATE-----"""
|
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""Set up an HTTP server to receive log messages, and a HTTPHandler
|
"""Set up an HTTP server to receive log messages, and a HTTPHandler
|
||||||
pointing to that server's address and port."""
|
pointing to that server's address and port."""
|
||||||
|
@ -1686,15 +1656,26 @@ IRbTpvlHWPjsSvHz0ZOH
|
||||||
if secure:
|
if secure:
|
||||||
try:
|
try:
|
||||||
import ssl
|
import ssl
|
||||||
fd, fn = tempfile.mkstemp()
|
|
||||||
os.close(fd)
|
|
||||||
with open(fn, 'w') as f:
|
|
||||||
f.write(self.PEMFILE)
|
|
||||||
sslctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
|
|
||||||
sslctx.load_cert_chain(fn)
|
|
||||||
os.unlink(fn)
|
|
||||||
except ImportError:
|
except ImportError:
|
||||||
sslctx = None
|
sslctx = None
|
||||||
|
else:
|
||||||
|
here = os.path.dirname(__file__)
|
||||||
|
localhost_cert = os.path.join(here, "keycert.pem")
|
||||||
|
sslctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
|
||||||
|
sslctx.load_cert_chain(localhost_cert)
|
||||||
|
# Unfortunately, HTTPHandler doesn't allow us to change the
|
||||||
|
# SSLContext used by HTTPSConnection, so we have to
|
||||||
|
# monkeypatch. This can be cleaned up if issue 22788 is
|
||||||
|
# fixed.
|
||||||
|
old = ssl._create_default_https_context
|
||||||
|
def restore_handler():
|
||||||
|
ssl._create_default_https_context = old
|
||||||
|
self.addCleanup(restore_handler)
|
||||||
|
def hack_create_ctx():
|
||||||
|
ctx = old()
|
||||||
|
ctx.load_verify_locations(localhost_cert)
|
||||||
|
return ctx
|
||||||
|
ssl._create_default_https_context = hack_create_ctx
|
||||||
else:
|
else:
|
||||||
sslctx = None
|
sslctx = None
|
||||||
self.server = server = TestHTTPServer(addr, self.handle_request,
|
self.server = server = TestHTTPServer(addr, self.handle_request,
|
||||||
|
|
|
@ -2582,9 +2582,10 @@ else:
|
||||||
d1 = f.read()
|
d1 = f.read()
|
||||||
d2 = ''
|
d2 = ''
|
||||||
# now fetch the same data from the HTTPS server
|
# now fetch the same data from the HTTPS server
|
||||||
url = 'https://%s:%d/%s' % (
|
url = 'https://localhost:%d/%s' % (
|
||||||
HOST, server.port, os.path.split(CERTFILE)[1])
|
server.port, os.path.split(CERTFILE)[1])
|
||||||
f = urllib.request.urlopen(url)
|
context = ssl.create_default_context(cafile=CERTFILE)
|
||||||
|
f = urllib.request.urlopen(url, context=context)
|
||||||
try:
|
try:
|
||||||
dlen = f.info().get("content-length")
|
dlen = f.info().get("content-length")
|
||||||
if dlen and (int(dlen) > 0):
|
if dlen and (int(dlen) > 0):
|
||||||
|
|
|
@ -545,7 +545,8 @@ class TestUrlopen(unittest.TestCase):
|
||||||
|
|
||||||
def test_https(self):
|
def test_https(self):
|
||||||
handler = self.start_https_server()
|
handler = self.start_https_server()
|
||||||
data = self.urlopen("https://localhost:%s/bizarre" % handler.port)
|
context = ssl.create_default_context(cafile=CERT_localhost)
|
||||||
|
data = self.urlopen("https://localhost:%s/bizarre" % handler.port, context=context)
|
||||||
self.assertEqual(data, b"we care a bit")
|
self.assertEqual(data, b"we care a bit")
|
||||||
|
|
||||||
def test_https_with_cafile(self):
|
def test_https_with_cafile(self):
|
||||||
|
@ -584,7 +585,8 @@ class TestUrlopen(unittest.TestCase):
|
||||||
context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
|
context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
|
||||||
context.set_servername_callback(cb_sni)
|
context.set_servername_callback(cb_sni)
|
||||||
handler = self.start_https_server(context=context, certfile=CERT_localhost)
|
handler = self.start_https_server(context=context, certfile=CERT_localhost)
|
||||||
self.urlopen("https://localhost:%s" % handler.port)
|
context = ssl.create_default_context(cafile=CERT_localhost)
|
||||||
|
self.urlopen("https://localhost:%s" % handler.port, context=context)
|
||||||
self.assertEqual(sni_name, "localhost")
|
self.assertEqual(sni_name, "localhost")
|
||||||
|
|
||||||
def test_sending_headers(self):
|
def test_sending_headers(self):
|
||||||
|
|
|
@ -183,6 +183,8 @@ Core and Builtins
|
||||||
Library
|
Library
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
- Issue #22417: Verify certificates by default in httplib (PEP 476).
|
||||||
|
|
||||||
- Issue #22775: Fixed unpickling of http.cookies.SimpleCookie with protocol 2
|
- Issue #22775: Fixed unpickling of http.cookies.SimpleCookie with protocol 2
|
||||||
and above. Patch by Tim Graham.
|
and above. Patch by Tim Graham.
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue