Commit e3e7d405 authored by Benjamin Peterson's avatar Benjamin Peterson

pep 476: verify certificates by default (#22417)

parent b206473e
...@@ -90,9 +90,6 @@ The module provides the following classes: ...@@ -90,9 +90,6 @@ The module provides the following classes:
server's certificate. If you want to change that behaviour, you can server's certificate. If you want to change that behaviour, you can
explicitly set *check_hostname* to False. explicitly set *check_hostname* to False.
.. warning::
This does not do any verification of the server's certificate.
.. versionadded:: 2.0 .. versionadded:: 2.0
.. versionchanged:: 2.6 .. versionchanged:: 2.6
...@@ -104,6 +101,11 @@ The module provides the following classes: ...@@ -104,6 +101,11 @@ The module provides the following classes:
.. versionchanged:: 2.7.9 .. versionchanged:: 2.7.9
*context* and *check_hostname* was added. *context* and *check_hostname* was added.
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, strict=0) .. class:: HTTPResponse(sock, debuglevel=0, strict=0)
......
...@@ -34,11 +34,10 @@ between conformable Python objects and XML on the wire. ...@@ -34,11 +34,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:: 2.7.9
In the case of https URIs, :mod:`xmlrpclib` does not do any verification of
the server's certificate.
For https URIs, :mod:`xmlrpclib` now performs all the necessary certificate
and hostname checks by default
.. class:: ServerProxy(uri[, transport[, encoding[, verbose[, allow_none[, use_datetime]]]]]) .. class:: ServerProxy(uri[, transport[, encoding[, verbose[, allow_none[, use_datetime]]]]])
......
...@@ -2646,6 +2646,29 @@ and :ref:`distutils-index`. ...@@ -2646,6 +2646,29 @@ and :ref:`distutils-index`.
PEP written by Donald Stufft and Nick Coghlan, implemented by PEP written by Donald Stufft and Nick Coghlan, implemented by
Donald Stufft, Nick Coghlan, Martin von Löwis and Ned Deily. Donald Stufft, Nick Coghlan, Martin von Löwis and Ned Deily.
PEP 476: Enabling certificate verification by default for stdlib http clients
-----------------------------------------------------------------------------
:mod:`httplib` and modules which use it, such as :mod:`urllib2` and
:mod:`xmlrpclib`, 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 urllib2
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")
urllib2.urlopen("https://invalid-cert", context=context)
.. ====================================================================== .. ======================================================================
......
...@@ -1193,7 +1193,7 @@ else: ...@@ -1193,7 +1193,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_default_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
......
...@@ -427,8 +427,7 @@ def create_default_context(purpose=Purpose.SERVER_AUTH, cafile=None, ...@@ -427,8 +427,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):
...@@ -469,6 +468,14 @@ def _create_stdlib_context(protocol=PROTOCOL_SSLv23, cert_reqs=None, ...@@ -469,6 +468,14 @@ 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 SSLSocket(socket): class SSLSocket(socket):
"""This class implements a subtype of socket.socket that wraps """This class implements a subtype of socket.socket that wraps
the underlying OS socket in an SSL context when necessary, and the underlying OS socket in an SSL context when necessary, and
......
import httplib import httplib
import array import array
import httplib
import os
import StringIO import StringIO
import socket import socket
import errno import errno
import os
import unittest import unittest
TestCase = unittest.TestCase TestCase = unittest.TestCase
......
...@@ -5,6 +5,7 @@ import urllib2 ...@@ -5,6 +5,7 @@ import urllib2
import BaseHTTPServer import BaseHTTPServer
import unittest import unittest
import hashlib import hashlib
import ssl
from test import test_support from test import test_support
...@@ -562,15 +563,37 @@ class TestUrlopen(BaseTestCase): ...@@ -562,15 +563,37 @@ class TestUrlopen(BaseTestCase):
cafile=CERT_localhost) cafile=CERT_localhost)
self.assertEqual(data, b"we care a bit") self.assertEqual(data, b"we care a bit")
# Bad cert # Bad cert
with self.assertRaises(urllib2.URLError) as cm: with self.assertRaises(urllib2.URLError):
self.urlopen("https://localhost:%s/bizarre" % handler.port, self.urlopen("https://localhost:%s/bizarre" % handler.port,
cafile=CERT_fakehostname) cafile=CERT_fakehostname)
# Good cert, but mismatching hostname # Good cert, but mismatching hostname
handler = self.start_https_server(certfile=CERT_fakehostname) handler = self.start_https_server(certfile=CERT_fakehostname)
with self.assertRaises(ssl.CertificateError) as cm: with self.assertRaises(ssl.CertificateError):
self.urlopen("https://localhost:%s/bizarre" % handler.port, self.urlopen("https://localhost:%s/bizarre" % handler.port,
cafile=CERT_fakehostname) cafile=CERT_fakehostname)
def test_https_with_cadefault(self):
handler = self.start_https_server(certfile=CERT_localhost)
# Self-signed cert should fail verification with system certificate store
with self.assertRaises(urllib2.URLError):
self.urlopen("https://localhost:%s/bizarre" % handler.port,
cadefault=True)
def test_https_sni(self):
if ssl is None:
self.skipTest("ssl module required")
if not ssl.HAS_SNI:
self.skipTest("SNI support required in OpenSSL")
sni_name = [None]
def cb_sni(ssl_sock, server_name, initial_context):
sni_name[0] = server_name
context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
context.set_servername_callback(cb_sni)
handler = self.start_https_server(context=context, certfile=CERT_localhost)
context = ssl.create_default_context(cafile=CERT_localhost)
self.urlopen("https://localhost:%s" % handler.port, context=context)
self.assertEqual(sni_name[0], "localhost")
def test_sending_headers(self): def test_sending_headers(self):
handler = self.start_server([(200, [], "we don't care")]) handler = self.start_server([(200, [], "we don't care")])
......
...@@ -42,6 +42,8 @@ Core and Builtins ...@@ -42,6 +42,8 @@ Core and Builtins
Library Library
------- -------
- Issue #22417: Verify certificates by default in httplib (PEP 476).
- Issue #22927: Allow urllib.urlopen to take a *context* parameter to control - Issue #22927: Allow urllib.urlopen to take a *context* parameter to control
SSL settings for HTTPS connections. SSL settings for HTTPS connections.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment