diff -r 687f0d4bc188 Lib/ssl.py --- a/Lib/ssl.py Thu Jan 14 21:57:57 2016 -0800 +++ b/Lib/ssl.py Fri Jan 15 17:41:37 2016 +1000 @@ -481,13 +481,30 @@ 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 -r 687f0d4bc188 Lib/test/test_ssl.py --- a/Lib/test/test_ssl.py Thu Jan 14 21:57:57 2016 -0800 +++ b/Lib/test/test_ssl.py Fri Jan 15 17:41:37 2016 +1000 @@ -1149,6 +1149,51 @@ 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 + with support.EnvironmentVarGuard() as env: + # Setting it to zero turns verification off + env[ssl._https_verify_envvar] = "0" + local_ssl = support.import_fresh_module("ssl") + self.assertIs(local_ssl._create_default_https_context, + local_ssl._create_unverified_context) + # Omitting it leaves verification on + del env[ssl._https_verify_envvar] + local_ssl = support.import_fresh_module("ssl") + self.assertIs(local_ssl._create_default_https_context, + local_ssl.create_default_context) + # Any other value should also leave it on + for setting in ("", "1", "enabled", "foo"): + env[ssl._https_verify_envvar] = setting + local_ssl = support.import_fresh_module("ssl") + self.assertIs(local_ssl._create_default_https_context, + local_ssl.create_default_context) + def test_check_hostname(self): ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) self.assertFalse(ctx.check_hostname)