Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bpo-43998: Default to TLS 1.2 and increase cipher suite security (GH-25778) #25778

Merged
merged 1 commit into from
May 1, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions Doc/library/ssl.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1509,6 +1509,14 @@ to speed up repeated connections from the same clients.
context class will either require :data:`PROTOCOL_TLS_CLIENT` or
:data:`PROTOCOL_TLS_SERVER` protocol in the future.

.. versionchanged:: 3.10

The default cipher suites now include only secure AES and ChaCha20
ciphers with forward secrecy and security level 2. RSA and DH keys with
less than 2048 bits and ECC keys with less than 224 bits are prohibited.
:data:`PROTOCOL_TLS`, :data:`PROTOCOL_TLS_CLIENT`, and
:data:`PROTOCOL_TLS_SERVER` use TLS 1.2 as minimum TLS version.


:class:`SSLContext` objects have the following methods and attributes:

Expand Down
6 changes: 5 additions & 1 deletion Doc/using/configure.rst
Original file line number Diff line number Diff line change
Expand Up @@ -441,12 +441,16 @@ Security Options

* ``python`` (default): use Python's preferred selection;
* ``openssl``: leave OpenSSL's defaults untouched;
* *STRING*: use a custom string, PROTOCOL_SSLv2 ignores the setting.
* *STRING*: use a custom string

See the :mod:`ssl` module.

.. versionadded:: 3.7

.. versionchanged:: 3.10

The settings ``python`` and *STRING* also set TLS 1.2 as minimum
protocol version.

macOS Options
-------------
Expand Down
7 changes: 7 additions & 0 deletions Doc/whatsnew/3.10.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1105,6 +1105,13 @@ Added option to create MPTCP sockets with ``IPPROTO_MPTCP``
ssl
---

The ssl module now has more secure default settings. Ciphers without forward
secrecy or SHA-1 MAC are disabled by default. Security level 2 prohibits
weak RSA, DH, and ECC keys with less than 112 bits of security.
:class:`~ssl.SSLContext` defaults to minimum protocol version TLS 1.2.
Settings are based on Hynek Schlawack's research.
(Contributed by Christian Heimes in :issue:`43998`.)

Add a *timeout* parameter to the :func:`ssl.get_server_certificate` function.
(Contributed by Zackery Spytz in :issue:`31870`.)

Expand Down
31 changes: 22 additions & 9 deletions Lib/test/test_nntplib.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ class SSLError(Exception):

class NetworkedNNTPTestsMixin:

ssl_context = None

def test_welcome(self):
welcome = self.server.getwelcome()
self.assertEqual(str, type(welcome))
Expand Down Expand Up @@ -273,18 +275,21 @@ def is_connected():
return False
return True

kwargs = dict(
timeout=support.INTERNET_TIMEOUT,
usenetrc=False
)
if self.ssl_context is not None:
kwargs["ssl_context"] = self.ssl_context

try:
server = self.NNTP_CLASS(self.NNTP_HOST,
timeout=support.INTERNET_TIMEOUT,
usenetrc=False)
server = self.NNTP_CLASS(self.NNTP_HOST, **kwargs)
with server:
self.assertTrue(is_connected())
self.assertTrue(server.help())
self.assertFalse(is_connected())

server = self.NNTP_CLASS(self.NNTP_HOST,
timeout=support.INTERNET_TIMEOUT,
usenetrc=False)
server = self.NNTP_CLASS(self.NNTP_HOST, **kwargs)
with server:
server.quit()
self.assertFalse(is_connected())
Expand Down Expand Up @@ -316,16 +321,21 @@ class NetworkedNNTPTests(NetworkedNNTPTestsMixin, unittest.TestCase):
@classmethod
def setUpClass(cls):
support.requires("network")
kwargs = dict(
timeout=support.INTERNET_TIMEOUT,
usenetrc=False
)
if cls.ssl_context is not None:
kwargs["ssl_context"] = cls.ssl_context
with socket_helper.transient_internet(cls.NNTP_HOST):
try:
cls.server = cls.NNTP_CLASS(cls.NNTP_HOST,
timeout=support.INTERNET_TIMEOUT,
usenetrc=False)
cls.server = cls.NNTP_CLASS(cls.NNTP_HOST, **kwargs)
except SSLError as ssl_err:
# matches "[SSL: DH_KEY_TOO_SMALL] dh key too small"
if re.search(r'(?i)KEY.TOO.SMALL', ssl_err.reason):
raise unittest.SkipTest(f"{cls} got {ssl_err} connecting "
f"to {cls.NNTP_HOST!r}")
print(cls.NNTP_HOST)
raise
except EOF_ERRORS:
raise unittest.SkipTest(f"{cls} got EOF error on connecting "
Expand Down Expand Up @@ -358,6 +368,9 @@ class NetworkedNNTP_SSLTests(NetworkedNNTPTests):
# Disabled as the connection will already be encrypted.
test_starttls = None

ssl_context = ssl._create_unverified_context()
ssl_context.set_ciphers("DEFAULT")
ssl_context.maximum_version = ssl.TLSVersion.TLSv1_2

#
# Non-networked tests using a local server (or something mocking it).
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
The :mod:`ssl` module sets more secure cipher suites defaults. Ciphers
without forward secrecy and with SHA-1 MAC are disabled by default. Security
level 2 prohibits weak RSA, DH, and ECC keys with less than 112 bits of
security. :class:`~ssl.SSLContext` defaults to minimum protocol version TLS
1.2. Settings are based on Hynek Schlawack's research.
43 changes: 38 additions & 5 deletions Modules/_ssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -152,15 +152,27 @@ extern const SSL_METHOD *TLSv1_2_method(void);
#ifndef PY_SSL_DEFAULT_CIPHER_STRING
#error "Py_SSL_DEFAULT_CIPHERS 0 needs Py_SSL_DEFAULT_CIPHER_STRING"
#endif
#ifndef PY_SSL_MIN_PROTOCOL
#define PY_SSL_MIN_PROTOCOL TLS1_2_VERSION
#endif
#elif PY_SSL_DEFAULT_CIPHERS == 1
/* Python custom selection of sensible cipher suites
* DEFAULT: OpenSSL's default cipher list. Since 1.0.2 the list is in sensible order.
* @SECLEVEL=2: security level 2 with 112 bits minimum security (e.g. 2048 bits RSA key)
* ECDH+*: enable ephemeral elliptic curve Diffie-Hellman
* DHE+*: fallback to ephemeral finite field Diffie-Hellman
* encryption order: AES AEAD (GCM), ChaCha AEAD, AES CBC
* !aNULL:!eNULL: really no NULL ciphers
* !MD5:!3DES:!DES:!RC4:!IDEA:!SEED: no weak or broken algorithms on old OpenSSL versions.
* !aDSS: no authentication with discrete logarithm DSA algorithm
* !SRP:!PSK: no secure remote password or pre-shared key authentication
* !SHA1: no weak SHA1 MAC
* !AESCCM: no CCM mode, it's uncommon and slow
*
* Based on Hynek's excellent blog post (update 2021-02-11)
* https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/
*/
#define PY_SSL_DEFAULT_CIPHER_STRING "DEFAULT:!aNULL:!eNULL:!MD5:!3DES:!DES:!RC4:!IDEA:!SEED:!aDSS:!SRP:!PSK"
#define PY_SSL_DEFAULT_CIPHER_STRING "@SECLEVEL=2:ECDH+AESGCM:ECDH+CHACHA20:ECDH+AES:DHE+AES:!aNULL:!eNULL:!aDSS:!SHA1:!AESCCM"
#ifndef PY_SSL_MIN_PROTOCOL
#define PY_SSL_MIN_PROTOCOL TLS1_2_VERSION
#endif
#elif PY_SSL_DEFAULT_CIPHERS == 2
/* Ignored in SSLContext constructor, only used to as _ssl.DEFAULT_CIPHER_STRING */
#define PY_SSL_DEFAULT_CIPHER_STRING SSL_DEFAULT_CIPHER_LIST
Expand Down Expand Up @@ -3095,8 +3107,25 @@ _ssl__SSLContext_impl(PyTypeObject *type, int proto_version)
ERR_clear_error();
PyErr_SetString(get_state_ctx(self)->PySSLErrorObject,
"No cipher can be selected.");
return NULL;
goto error;
}
#ifdef PY_SSL_MIN_PROTOCOL
switch(proto_version) {
case PY_SSL_VERSION_TLS:
case PY_SSL_VERSION_TLS_CLIENT:
case PY_SSL_VERSION_TLS_SERVER:
result = SSL_CTX_set_min_proto_version(ctx, PY_SSL_MIN_PROTOCOL);
if (result == 0) {
PyErr_Format(PyExc_ValueError,
"Failed to set minimum protocol 0x%x",
PY_SSL_MIN_PROTOCOL);
goto error;
}
break;
default:
break;
}
#endif

/* Set SSL_MODE_RELEASE_BUFFERS. This potentially greatly reduces memory
usage for no cost at all. */
Expand All @@ -3119,6 +3148,10 @@ _ssl__SSLContext_impl(PyTypeObject *type, int proto_version)
#endif

return (PyObject *)self;
error:
Py_XDECREF(self);
ERR_clear_error();
return NULL;
}

static int
Expand Down
4 changes: 2 additions & 2 deletions configure
Original file line number Diff line number Diff line change
Expand Up @@ -1604,8 +1604,8 @@ Optional Packages:
override default cipher suites string, python: use
Python's preferred selection (default), openssl:
leave OpenSSL's defaults untouched, STRING: use a
custom string, PROTOCOL_SSLv2 ignores the setting,
see Doc/library/ssl.rst
custom string, python and STRING also set TLS 1.2 as
minimum TLS version
--with-builtin-hashlib-hashes=md5,sha1,sha256,sha512,sha3,blake2
builtin hash modules, md5, sha1, sha256, sha512,
sha3 (with shake), blake2
Expand Down
2 changes: 1 addition & 1 deletion configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -5836,7 +5836,7 @@ AC_ARG_WITH(ssl-default-suites,
python: use Python's preferred selection (default),
openssl: leave OpenSSL's defaults untouched,
STRING: use a custom string,
PROTOCOL_SSLv2 ignores the setting, see Doc/library/ssl.rst]),
python and STRING also set TLS 1.2 as minimum TLS version]),
[
AC_MSG_RESULT($withval)
case "$withval" in
Expand Down