mirror of
				https://github.com/python/cpython.git
				synced 2025-10-29 20:51:26 +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 | ||||
|    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. | ||||
| 
 | ||||
|    .. note:: | ||||
|  | @ -97,6 +91,12 @@ The module provides the following classes: | |||
|       The *strict* parameter was removed. HTTP 0.9-style "Simple Responses" are | ||||
|       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) | ||||
| 
 | ||||
|  |  | |||
|  | @ -62,11 +62,6 @@ The :mod:`urllib.request` module defines the following functions: | |||
|    *cafile* and *capath* parameters are omitted.  This will only work on | ||||
|    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 | ||||
|    :class:`http.client.HTTPResponse` object which has the following | ||||
|    :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 | ||||
|    :ref:`xml-vulnerabilities`. | ||||
| 
 | ||||
| .. warning:: | ||||
| 
 | ||||
|    In the case of https URIs, :mod:`xmlrpc.client` does not do any verification | ||||
|    of the server's certificate. | ||||
| .. versionchanged:: 3.4.3 | ||||
| 
 | ||||
|    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, \ | ||||
|                        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` | ||||
|   structure has been removed to fix a bug: see :issue:`14432` for the | ||||
|   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 @@ def __init__(self, host, port=None, key_file=None, cert_file=None, | |||
|             self.key_file = key_file | ||||
|             self.cert_file = cert_file | ||||
|             if context is None: | ||||
|                 context = ssl._create_stdlib_context() | ||||
|                 context = ssl._create_default_https_context() | ||||
|             will_verify = context.verify_mode != ssl.CERT_NONE | ||||
|             if check_hostname is None: | ||||
|                 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) | ||||
|     return context | ||||
| 
 | ||||
| 
 | ||||
| def _create_stdlib_context(protocol=PROTOCOL_SSLv23, *, cert_reqs=None, | ||||
| def _create_unverified_context(protocol=PROTOCOL_SSLv23, *, cert_reqs=None, | ||||
|                            check_hostname=False, purpose=Purpose.SERVER_AUTH, | ||||
|                            certfile=None, keyfile=None, | ||||
|                            cafile=None, capath=None, cadata=None): | ||||
|  | @ -478,6 +477,13 @@ def _create_stdlib_context(protocol=PROTOCOL_SSLv23, *, cert_reqs=None, | |||
| 
 | ||||
|     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: | ||||
|     """This class implements an interface on top of a low-level SSL object as | ||||
|  |  | |||
|  | @ -1012,13 +1012,36 @@ def _check_svn_python_org(self, resp): | |||
|         self.assertIn('Apache', server_string) | ||||
| 
 | ||||
|     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') | ||||
|         with support.transient_internet('svn.python.org'): | ||||
|             h = client.HTTPSConnection('svn.python.org', 443) | ||||
|         with support.transient_internet('self-signed.pythontest.net'): | ||||
|             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', '/') | ||||
|             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): | ||||
|         # We feed a CA cert that validates the server's cert | ||||
|  | @ -1037,13 +1060,23 @@ def test_networked_bad_cert(self): | |||
|         # We feed a "CA" cert that is unrelated to the server's cert | ||||
|         import ssl | ||||
|         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.verify_mode = ssl.CERT_REQUIRED | ||||
|             context.load_verify_locations(CERT_localhost) | ||||
|             h = client.HTTPSConnection('svn.python.org', 443, context=context) | ||||
|             with self.assertRaises(ssl.SSLError): | ||||
|             h = client.HTTPSConnection('self-signed.pythontest.net', 443, context=context) | ||||
|             with self.assertRaises(ssl.SSLError) as exc_info: | ||||
|                 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): | ||||
|         # The (valid) cert validates the HTTP hostname | ||||
|  | @ -1056,7 +1089,6 @@ def test_local_good_hostname(self): | |||
|         h.request('GET', '/nonexistent') | ||||
|         resp = h.getresponse() | ||||
|         self.assertEqual(resp.status, 404) | ||||
|         del server | ||||
| 
 | ||||
|     def test_local_bad_hostname(self): | ||||
|         # The (valid) cert doesn't validate the HTTP hostname | ||||
|  | @ -1079,7 +1111,6 @@ def test_local_bad_hostname(self): | |||
|         h.request('GET', '/nonexistent') | ||||
|         resp = h.getresponse() | ||||
|         self.assertEqual(resp.status, 404) | ||||
|         del server | ||||
| 
 | ||||
|     @unittest.skipIf(not hasattr(client, 'HTTPSConnection'), | ||||
|                      'http.client.HTTPSConnection not available') | ||||
|  |  | |||
|  | @ -1627,36 +1627,6 @@ def tearDown(self): | |||
| class HTTPHandlerTest(BaseTest): | ||||
|     """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): | ||||
|         """Set up an HTTP server to receive log messages, and a HTTPHandler | ||||
|         pointing to that server's address and port.""" | ||||
|  | @ -1686,15 +1656,26 @@ def test_output(self): | |||
|             if secure: | ||||
|                 try: | ||||
|                     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: | ||||
|                     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: | ||||
|                 sslctx = None | ||||
|             self.server = server = TestHTTPServer(addr, self.handle_request, | ||||
|  |  | |||
|  | @ -2582,9 +2582,10 @@ def test_socketserver(self): | |||
|                 d1 = f.read() | ||||
|             d2 = '' | ||||
|             # now fetch the same data from the HTTPS server | ||||
|             url = 'https://%s:%d/%s' % ( | ||||
|                 HOST, server.port, os.path.split(CERTFILE)[1]) | ||||
|             f = urllib.request.urlopen(url) | ||||
|             url = 'https://localhost:%d/%s' % ( | ||||
|                 server.port, os.path.split(CERTFILE)[1]) | ||||
|             context = ssl.create_default_context(cafile=CERTFILE) | ||||
|             f = urllib.request.urlopen(url, context=context) | ||||
|             try: | ||||
|                 dlen = f.info().get("content-length") | ||||
|                 if dlen and (int(dlen) > 0): | ||||
|  |  | |||
|  | @ -545,7 +545,8 @@ def test_200_with_parameters(self): | |||
| 
 | ||||
|     def test_https(self): | ||||
|         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") | ||||
| 
 | ||||
|     def test_https_with_cafile(self): | ||||
|  | @ -584,7 +585,8 @@ def cb_sni(ssl_sock, server_name, initial_context): | |||
|         context = ssl.SSLContext(ssl.PROTOCOL_TLSv1) | ||||
|         context.set_servername_callback(cb_sni) | ||||
|         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") | ||||
| 
 | ||||
|     def test_sending_headers(self): | ||||
|  |  | |||
|  | @ -183,6 +183,8 @@ Core and Builtins | |||
| Library | ||||
| ------- | ||||
| 
 | ||||
| - Issue #22417: Verify certificates by default in httplib (PEP 476). | ||||
| 
 | ||||
| - Issue #22775: Fixed unpickling of http.cookies.SimpleCookie with protocol 2 | ||||
|   and above.  Patch by Tim Graham. | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Benjamin Peterson
						Benjamin Peterson