diff options
author | Christian Heimes <christian@cheimes.de> | 2013-11-23 15:58:30 +0100 |
---|---|---|
committer | Christian Heimes <christian@cheimes.de> | 2013-11-23 15:58:30 +0100 |
commit | f00a68c78acd873a73a3be7bbcc4205410cc84f8 (patch) | |
tree | 6c308d6b1421963294fa61a78b78b46f1119244e /Lib | |
parent | 3ba686e7ff938fac60624b695079be0959d3cb10 (diff) | |
download | cpython-f00a68c78acd873a73a3be7bbcc4205410cc84f8.tar.gz |
Issue #19689: Add ssl.create_default_context() factory function. It creates
a new SSLContext object with secure default settings.
Diffstat (limited to 'Lib')
-rw-r--r-- | Lib/ssl.py | 35 | ||||
-rw-r--r-- | Lib/test/test_ssl.py | 20 |
2 files changed, 55 insertions, 0 deletions
diff --git a/Lib/ssl.py b/Lib/ssl.py index e668dc181f..880a3d4af6 100644 --- a/Lib/ssl.py +++ b/Lib/ssl.py @@ -165,6 +165,13 @@ else: # (OpenSSL's default setting is 'DEFAULT:!aNULL:!eNULL') _DEFAULT_CIPHERS = 'DEFAULT:!aNULL:!eNULL:!LOW:!EXPORT:!SSLv2' +# restricted and more secure ciphers +# HIGH: high encryption cipher suites with key length >= 128 bits (no MD5) +# !aNULL: only authenticated cipher suites (no anonymous DH) +# !RC4: no RC4 streaming cipher, RC4 is broken +# !DSS: RSA is preferred over DSA +_RESTRICTED_CIPHERS = 'HIGH:!aNULL:!RC4:!DSS' + class CertificateError(ValueError): pass @@ -363,6 +370,34 @@ class SSLContext(_SSLContext): self.set_default_verify_paths() +def create_default_context(purpose=Purpose.SERVER_AUTH, *, cafile=None, + capath=None, cadata=None): + """Create a SSLContext object with default settings. + + NOTE: The protocol and settings may change anytime without prior + deprecation. The values represent a fair balance between maximum + compatibility and security. + """ + if not isinstance(purpose, _ASN1Object): + raise TypeError(purpose) + context = SSLContext(PROTOCOL_TLSv1) + # SSLv2 considered harmful. + context.options |= OP_NO_SSLv2 + # disallow ciphers with known vulnerabilities + context.set_ciphers(_RESTRICTED_CIPHERS) + # verify certs in client mode + if purpose == Purpose.SERVER_AUTH: + context.verify_mode = CERT_REQUIRED + if cafile or capath or cadata: + context.load_verify_locations(cafile, capath, cadata) + elif context.verify_mode != CERT_NONE: + # no explicit cafile, capath or cadata but the verify mode is + # CERT_OPTIONAL or CERT_REQUIRED. Let's try to load default system + # root CA certificates for the given purpose. This may fail silently. + context.load_default_certs(purpose) + return context + + class SSLSocket(socket): """This class implements a subtype of socket.socket that wraps the underlying OS socket in an SSL context when necessary, and diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index 722d331f27..0b7ea477d6 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -999,6 +999,26 @@ class ContextTests(unittest.TestCase): self.assertRaises(TypeError, ctx.load_default_certs, None) self.assertRaises(TypeError, ctx.load_default_certs, 'SERVER_AUTH') + def test_create_default_context(self): + ctx = ssl.create_default_context() + self.assertEqual(ctx.protocol, ssl.PROTOCOL_TLSv1) + self.assertEqual(ctx.verify_mode, ssl.CERT_REQUIRED) + self.assertEqual(ctx.options & ssl.OP_NO_SSLv2, ssl.OP_NO_SSLv2) + + with open(SIGNING_CA) as f: + cadata = f.read() + ctx = ssl.create_default_context(cafile=SIGNING_CA, capath=CAPATH, + cadata=cadata) + self.assertEqual(ctx.protocol, ssl.PROTOCOL_TLSv1) + self.assertEqual(ctx.verify_mode, ssl.CERT_REQUIRED) + self.assertEqual(ctx.options & ssl.OP_NO_SSLv2, ssl.OP_NO_SSLv2) + + ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) + self.assertEqual(ctx.protocol, ssl.PROTOCOL_TLSv1) + self.assertEqual(ctx.verify_mode, ssl.CERT_NONE) + self.assertEqual(ctx.options & ssl.OP_NO_SSLv2, ssl.OP_NO_SSLv2) + + class SSLErrorTests(unittest.TestCase): |