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:
Donald Stufft 2014-03-23 19:05:28 -04:00
parent 553e108fce
commit 6a2ba94908
4 changed files with 70 additions and 16 deletions

View File

@ -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

View File

@ -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:

View File

@ -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()

View File

@ -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.