Issue #21013: Enhance ssl.create_default_context() for server side contexts
Closes #21013 by modfying ssl.create_default_context() to: * Move the restricted ciphers to only apply when using ssl.Purpose.CLIENT_AUTH. The major difference between restricted and not is the lack of RC4 in the restricted. However there are servers that exist that only expose RC4 still. * Switches the default protocol to ssl.PROTOCOL_SSLv23 so that the context will select TLS1.1 or TLS1.2 if it is available. * Add ssl.OP_NO_SSLv3 by default to continue to block SSL3.0 sockets * Add ssl.OP_SINGLE_DH_USE and ssl.OP_SINGLE_ECDG_USE to improve the security of the perfect forward secrecy * Add ssl.OP_CIPHER_SERVER_PREFERENCE so that when used for a server side socket the context will prioritize our ciphers which have been carefully selected to maximize security and performance. * Documents the failure conditions when a SSL3.0 connection is required so that end users can more easily determine if they need to unset ssl.OP_NO_SSLv3.
This commit is contained in:
parent
553e108fce
commit
6a2ba94908
|
@ -250,13 +250,13 @@ purposes.
|
|||
:const:`None`, this function can choose to trust the system's default
|
||||
CA certificates instead.
|
||||
|
||||
The settings in Python 3.4 are: :data:`PROTOCOL_TLSv1` with high encryption
|
||||
cipher suites without RC4 and without unauthenticated cipher suites.
|
||||
Passing :data:`~Purpose.SERVER_AUTH` as *purpose* sets
|
||||
:data:`~SSLContext.verify_mode` to :data:`CERT_REQUIRED` and either
|
||||
loads CA certificates (when at least one of *cafile*, *capath* or *cadata*
|
||||
is given) or uses :meth:`SSLContext.load_default_certs` to load default
|
||||
CA certificates.
|
||||
The settings in Python 3.4 are: :data:`PROTOCOL_SSLv23`, :data:`OP_NO_SSLv2`,
|
||||
and :data:`OP_NO_SSLv3` with high encryption cipher suites without RC4 and
|
||||
without unauthenticated cipher suites. Passing :data:`~Purpose.SERVER_AUTH`
|
||||
as *purpose* sets :data:`~SSLContext.verify_mode` to :data:`CERT_REQUIRED`
|
||||
and either loads CA certificates (when at least one of *cafile*, *capath* or
|
||||
*cadata* is given) or uses :meth:`SSLContext.load_default_certs` to load
|
||||
default CA certificates.
|
||||
|
||||
.. note::
|
||||
The protocol, options, cipher and other settings may change to more
|
||||
|
@ -266,6 +266,19 @@ purposes.
|
|||
If your application needs specific settings, you should create a
|
||||
:class:`SSLContext` and apply the settings yourself.
|
||||
|
||||
.. note::
|
||||
If you find that when certain older clients or servers attempt to connect
|
||||
with a :class:`SSLContext` created by this function that they get an
|
||||
error stating "Protocol or cipher suite mismatch", it may be that they
|
||||
only support SSL3.0 which this function excludes using the
|
||||
:data:`OP_NO_SSLv3`. SSL3.0 has problematic security due to a number of
|
||||
poor implementations and it's reliance on MD5 within the protocol. If you
|
||||
wish to continue to use this function but still allow SSL 3.0 connections
|
||||
you can re-enable them using::
|
||||
|
||||
ctx = ssl.create_default_context(Purpose.CLIENT_AUTH)
|
||||
ctx.options &= ~ssl.OP_NO_SSLv3
|
||||
|
||||
.. versionadded:: 3.4
|
||||
|
||||
|
||||
|
|
30
Lib/ssl.py
30
Lib/ssl.py
|
@ -179,7 +179,7 @@ _DEFAULT_CIPHERS = (
|
|||
'DH+RC4:RSA+RC4:!aNULL:!eNULL:!MD5'
|
||||
)
|
||||
|
||||
# Restricted and more secure ciphers
|
||||
# Restricted and more secure ciphers for the server side
|
||||
# This list has been explicitly chosen to:
|
||||
# * Prefer cipher suites that offer perfect forward secrecy (DHE/ECDHE)
|
||||
# * Prefer ECDHE over DHE for better performance
|
||||
|
@ -188,7 +188,7 @@ _DEFAULT_CIPHERS = (
|
|||
# * Then Use 3DES as fallback which is secure but slow
|
||||
# * Disable NULL authentication, NULL encryption, MD5 MACs, DSS, and RC4 for
|
||||
# security reasons
|
||||
_RESTRICTED_CIPHERS = (
|
||||
_RESTRICTED_SERVER_CIPHERS = (
|
||||
'ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+HIGH:'
|
||||
'DH+HIGH:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+HIGH:RSA+3DES:!aNULL:'
|
||||
'!eNULL:!MD5:!DSS:!RC4'
|
||||
|
@ -404,17 +404,35 @@ def create_default_context(purpose=Purpose.SERVER_AUTH, *, cafile=None,
|
|||
"""
|
||||
if not isinstance(purpose, _ASN1Object):
|
||||
raise TypeError(purpose)
|
||||
context = SSLContext(PROTOCOL_TLSv1)
|
||||
|
||||
context = SSLContext(PROTOCOL_SSLv23)
|
||||
|
||||
# SSLv2 considered harmful.
|
||||
context.options |= OP_NO_SSLv2
|
||||
|
||||
# SSLv3 has problematic security and is only required for really old
|
||||
# clients such as IE6 on Windows XP
|
||||
context.options |= OP_NO_SSLv3
|
||||
|
||||
# disable compression to prevent CRIME attacks (OpenSSL 1.0+)
|
||||
context.options |= getattr(_ssl, "OP_NO_COMPRESSION", 0)
|
||||
# disallow ciphers with known vulnerabilities
|
||||
context.set_ciphers(_RESTRICTED_CIPHERS)
|
||||
# verify certs and host name in client mode
|
||||
|
||||
if purpose == Purpose.SERVER_AUTH:
|
||||
# verify certs and host name in client mode
|
||||
context.verify_mode = CERT_REQUIRED
|
||||
context.check_hostname = True
|
||||
elif purpose == Purpose.CLIENT_AUTH:
|
||||
# Prefer the server's ciphers by default so that we get stronger
|
||||
# encryption
|
||||
context.options |= getattr(_ssl, "OP_CIPHER_SERVER_PREFERENCE", 0)
|
||||
|
||||
# Use single use keys in order to improve forward secrecy
|
||||
context.options |= getattr(_ssl, "OP_SINGLE_DH_USE", 0)
|
||||
context.options |= getattr(_ssl, "OP_SINGLE_ECDH_USE", 0)
|
||||
|
||||
# disallow ciphers with known vulnerabilities
|
||||
context.set_ciphers(_RESTRICTED_SERVER_CIPHERS)
|
||||
|
||||
if cafile or capath or cadata:
|
||||
context.load_verify_locations(cafile, capath, cadata)
|
||||
elif context.verify_mode != CERT_NONE:
|
||||
|
|
|
@ -1014,23 +1014,43 @@ class ContextTests(unittest.TestCase):
|
|||
|
||||
def test_create_default_context(self):
|
||||
ctx = ssl.create_default_context()
|
||||
self.assertEqual(ctx.protocol, ssl.PROTOCOL_TLSv1)
|
||||
self.assertEqual(ctx.protocol, ssl.PROTOCOL_SSLv23)
|
||||
self.assertEqual(ctx.verify_mode, ssl.CERT_REQUIRED)
|
||||
self.assertTrue(ctx.check_hostname)
|
||||
self.assertEqual(ctx.options & ssl.OP_NO_SSLv2, ssl.OP_NO_SSLv2)
|
||||
self.assertEqual(
|
||||
ctx.options & getattr(ssl, "OP_NO_COMPRESSION", 0),
|
||||
getattr(ssl, "OP_NO_COMPRESSION", 0),
|
||||
)
|
||||
|
||||
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.protocol, ssl.PROTOCOL_SSLv23)
|
||||
self.assertEqual(ctx.verify_mode, ssl.CERT_REQUIRED)
|
||||
self.assertEqual(ctx.options & ssl.OP_NO_SSLv2, ssl.OP_NO_SSLv2)
|
||||
self.assertEqual(
|
||||
ctx.options & getattr(ssl, "OP_NO_COMPRESSION", 0),
|
||||
getattr(ssl, "OP_NO_COMPRESSION", 0),
|
||||
)
|
||||
|
||||
ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
|
||||
self.assertEqual(ctx.protocol, ssl.PROTOCOL_TLSv1)
|
||||
self.assertEqual(ctx.protocol, ssl.PROTOCOL_SSLv23)
|
||||
self.assertEqual(ctx.verify_mode, ssl.CERT_NONE)
|
||||
self.assertEqual(ctx.options & ssl.OP_NO_SSLv2, ssl.OP_NO_SSLv2)
|
||||
self.assertEqual(
|
||||
ctx.options & getattr(ssl, "OP_NO_COMPRESSION", 0),
|
||||
getattr(ssl, "OP_NO_COMPRESSION", 0),
|
||||
)
|
||||
self.assertEqual(
|
||||
ctx.options & getattr(ssl, "OP_SINGLE_DH_USE", 0),
|
||||
getattr(ssl, "OP_SINGLE_DH_USE", 0),
|
||||
)
|
||||
self.assertEqual(
|
||||
ctx.options & getattr(ssl, "OP_SINGLE_ECDH_USE", 0),
|
||||
getattr(ssl, "OP_SINGLE_ECDH_USE", 0),
|
||||
)
|
||||
|
||||
def test__create_stdlib_context(self):
|
||||
ctx = ssl._create_stdlib_context()
|
||||
|
|
|
@ -24,6 +24,9 @@ Core and Builtins
|
|||
Library
|
||||
-------
|
||||
|
||||
- Issue #21013: Enhance ssl.create_default_context() when used for server side
|
||||
sockets to provide better security by default.
|
||||
|
||||
- Issue #20633: Replace relative import by absolute import.
|
||||
|
||||
- Issue #20980: Stop wrapping exception when using ThreadPool.
|
||||
|
|
Loading…
Reference in New Issue