diff options
author | Nick Coghlan <ncoghlan@gmail.com> | 2016-03-20 12:39:15 (GMT) |
---|---|---|
committer | Nick Coghlan <ncoghlan@gmail.com> | 2016-03-20 12:39:15 (GMT) |
commit | dbcd4576244b9c9acc6201034b1dfcc858c541ed (patch) | |
tree | b7c797fa121078fe2fd39fc04dcde8bb4ec82ab7 /Lib | |
parent | 3a4bdb6322fb9cf52baebf5c5ab72c0f8b52a18a (diff) | |
download | cpython-dbcd4576244b9c9acc6201034b1dfcc858c541ed.zip cpython-dbcd4576244b9c9acc6201034b1dfcc858c541ed.tar.gz cpython-dbcd4576244b9c9acc6201034b1dfcc858c541ed.tar.bz2 |
Issue #23857: Implement PEP 493
Adds a Python-2-only ssl module API and environment variable to
configure the default handling of SSL/TLS certificates for
HTTPS connections.
Diffstat (limited to 'Lib')
-rw-r--r-- | Lib/ssl.py | 25 | ||||
-rw-r--r-- | Lib/test/test_ssl.py | 52 |
2 files changed, 73 insertions, 4 deletions
@@ -482,13 +482,30 @@ def _create_unverified_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 +# PEP 493: Verify HTTPS by default, but allow envvar to override that +_https_verify_envvar = 'PYTHONHTTPSVERIFY' + +def _get_https_context_factory(): + if not sys.flags.ignore_environment: + config_setting = os.environ.get(_https_verify_envvar) + if config_setting == '0': + return _create_unverified_context + return create_default_context + +_create_default_https_context = _get_https_context_factory() + +# PEP 493: "private" API to configure HTTPS defaults without monkeypatching +def _https_verify_certificates(enable=True): + """Verify server HTTPS certificates by default?""" + global _create_default_https_context + if enable: + _create_default_https_context = create_default_context + else: + _create_default_https_context = _create_unverified_context + class SSLSocket(socket): """This class implements a subtype of socket.socket that wraps diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index e9723a7..86ba655 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -4,6 +4,7 @@ import sys import unittest from test import test_support as support +from test.script_helper import assert_python_ok import asyncore import socket import select @@ -1174,6 +1175,57 @@ class ContextTests(unittest.TestCase): self.assertEqual(ctx.verify_mode, ssl.CERT_NONE) self.assertEqual(ctx.options & ssl.OP_NO_SSLv2, ssl.OP_NO_SSLv2) + def test__https_verify_certificates(self): + # Unit test to check the contect factory mapping + # The factories themselves are tested above + # This test will fail by design if run under PYTHONHTTPSVERIFY=0 + # (as will various test_httplib tests) + + # Uses a fresh SSL module to avoid affecting the real one + local_ssl = support.import_fresh_module("ssl") + # Certificate verification is enabled by default + self.assertIs(local_ssl._create_default_https_context, + local_ssl.create_default_context) + # Turn default verification off + local_ssl._https_verify_certificates(enable=False) + self.assertIs(local_ssl._create_default_https_context, + local_ssl._create_unverified_context) + # And back on + local_ssl._https_verify_certificates(enable=True) + self.assertIs(local_ssl._create_default_https_context, + local_ssl.create_default_context) + # The default behaviour is to enable + local_ssl._https_verify_certificates(enable=False) + local_ssl._https_verify_certificates() + self.assertIs(local_ssl._create_default_https_context, + local_ssl.create_default_context) + + def test__https_verify_envvar(self): + # Unit test to check the PYTHONHTTPSVERIFY handling + # Need to use a subprocess so it can still be run under -E + https_is_verified = """import ssl, sys; \ + status = "Error: _create_default_https_context does not verify certs" \ + if ssl._create_default_https_context is \ + ssl._create_unverified_context \ + else None; \ + sys.exit(status)""" + https_is_not_verified = """import ssl, sys; \ + status = "Error: _create_default_https_context verifies certs" \ + if ssl._create_default_https_context is \ + ssl.create_default_context \ + else None; \ + sys.exit(status)""" + extra_env = {} + # Omitting it leaves verification on + assert_python_ok("-c", https_is_verified, **extra_env) + # Setting it to zero turns verification off + extra_env[ssl._https_verify_envvar] = "0" + assert_python_ok("-c", https_is_not_verified, **extra_env) + # Any other value should also leave it on + for setting in ("", "1", "enabled", "foo"): + extra_env[ssl._https_verify_envvar] = setting + assert_python_ok("-c", https_is_verified, **extra_env) + def test_check_hostname(self): ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) self.assertFalse(ctx.check_hostname) |