aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Doc/library/ssl.rst8
-rw-r--r--Lib/test/test_nntplib.py31
-rw-r--r--Misc/NEWS.d/next/Security/2021-05-01-13-13-40.bpo-43998.xhmWD7.rst5
-rw-r--r--Modules/_ssl.c43
-rwxr-xr-xconfigure4
-rw-r--r--configure.ac2
6 files changed, 76 insertions, 17 deletions
diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst
index af9bf798da3..0166fe3caca 100644
--- a/Doc/library/ssl.rst
+++ b/Doc/library/ssl.rst
@@ -1471,6 +1471,14 @@ to speed up repeated connections from the same clients.
ciphers, no ``NULL`` ciphers and no ``MD5`` ciphers (except for
:data:`PROTOCOL_SSLv2`).
+ .. versionchanged:: 3.9.5_p2 (Gentoo)
+
+ 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:
diff --git a/Lib/test/test_nntplib.py b/Lib/test/test_nntplib.py
index 99386ddbaed..cc7c93821d7 100644
--- a/Lib/test/test_nntplib.py
+++ b/Lib/test/test_nntplib.py
@@ -37,6 +37,8 @@ else:
class NetworkedNNTPTestsMixin:
+ ssl_context = None
+
def test_welcome(self):
welcome = self.server.getwelcome()
self.assertEqual(str, type(welcome))
@@ -273,18 +275,21 @@ class NetworkedNNTPTestsMixin:
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())
@@ -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 "
@@ -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).
diff --git a/Misc/NEWS.d/next/Security/2021-05-01-13-13-40.bpo-43998.xhmWD7.rst b/Misc/NEWS.d/next/Security/2021-05-01-13-13-40.bpo-43998.xhmWD7.rst
new file mode 100644
index 00000000000..6a40346128e
--- /dev/null
+++ b/Misc/NEWS.d/next/Security/2021-05-01-13-13-40.bpo-43998.xhmWD7.rst
@@ -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.
diff --git a/Modules/_ssl.c b/Modules/_ssl.c
index 52bd6841faf..c39d6ba4e95 100644
--- a/Modules/_ssl.c
+++ b/Modules/_ssl.c
@@ -298,15 +298,27 @@ SSL_SESSION_get_ticket_lifetime_hint(const SSL_SESSION *s)
#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
@@ -3251,8 +3263,25 @@ _ssl__SSLContext_impl(PyTypeObject *type, int proto_version)
ERR_clear_error();
PyErr_SetString(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
#if defined(SSL_MODE_RELEASE_BUFFERS)
/* Set SSL_MODE_RELEASE_BUFFERS. This potentially greatly reduces memory
@@ -3305,6 +3334,10 @@ _ssl__SSLContext_impl(PyTypeObject *type, int proto_version)
#endif
return (PyObject *)self;
+ error:
+ Py_XDECREF(self);
+ ERR_clear_error();
+ return NULL;
}
static int
diff --git a/configure b/configure
index b7be60eaa37..a6c70d3ee16 100755
--- a/configure
+++ b/configure
@@ -1596,8 +1596,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
diff --git a/configure.ac b/configure.ac
index aa515da4655..a760b508986 100644
--- a/configure.ac
+++ b/configure.ac
@@ -5807,7 +5807,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