diff options
author | Seth Michael Larson <sethmichaellarson@gmail.com> | 2020-10-06 21:29:11 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-10-06 21:29:11 -0500 |
commit | 4b0da49f8f1e485e34ddf6ffed93db4e452dc6a7 (patch) | |
tree | 8053082ca398691810d92d890b729fbe550853b8 | |
parent | 4fb7f3252b92bcc50cc8fa753f9dc6c147a6b2c0 (diff) | |
download | urllib3-4b0da49f8f1e485e34ddf6ffed93db4e452dc6a7.tar.gz |
Deprecate negotiating TLSv1 and TLSv1.1 by default
-rw-r--r-- | src/urllib3/connection.py | 17 | ||||
-rw-r--r-- | test/with_dummyserver/test_https.py | 120 |
2 files changed, 117 insertions, 20 deletions
diff --git a/src/urllib3/connection.py b/src/urllib3/connection.py index f3336acf..1c55f0eb 100644 --- a/src/urllib3/connection.py +++ b/src/urllib3/connection.py @@ -410,6 +410,23 @@ class HTTPSConnection(HTTPConnection): tls_in_tls=tls_in_tls, ) + # If we're using all defaults and the connection + # is TLSv1 or TLSv1.1 we throw a DeprecationWarning + # for the host. + if ( + default_ssl_context + and self.ssl_version is None + and hasattr(self.sock, "version") + and self.sock.version() in {"TLSv1", "TLSv1.1"} + ): + warnings.warn( + "Negotiating TLSv1/TLSv1.1 by default is deprecated " + "and will be disabled in urllib3 v2.0.0. Connecting to " + "'%s' with '%s' can be enabled by explicitly opting-in " + "with 'ssl_version'" % (self.host, self.sock.version()), + DeprecationWarning, + ) + if self.assert_fingerprint: assert_fingerprint( self.sock.getpeercert(binary_form=True), self.assert_fingerprint diff --git a/test/with_dummyserver/test_https.py b/test/with_dummyserver/test_https.py index 40998f0f..69d05bf7 100644 --- a/test/with_dummyserver/test_https.py +++ b/test/with_dummyserver/test_https.py @@ -87,6 +87,9 @@ CLIENT_CERT = CLIENT_INTERMEDIATE_PEM class TestHTTPS(HTTPSDummyServerTestCase): tls_protocol_name = None + def tls_protocol_deprecated(self): + return self.tls_protocol_name in {"TLSv1", "TLSv1.1"} + @classmethod def setup_class(cls): super(TestHTTPS, cls).setup_class() @@ -213,26 +216,25 @@ class TestHTTPS(HTTPSDummyServerTestCase): conn = https_pool._new_conn() assert conn.__class__ == VerifiedHTTPSConnection - with mock.patch("warnings.warn") as warn: + with warnings.catch_warnings(record=True) as w: r = https_pool.request("GET", "/") assert r.status == 200 - # Modern versions of Python, or systems using PyOpenSSL, don't - # emit warnings. - if ( - sys.version_info >= (2, 7, 9) - or util.IS_PYOPENSSL - or util.IS_SECURETRANSPORT - ): - assert not warn.called, warn.call_args_list - else: - assert warn.called - if util.HAS_SNI: - call = warn.call_args_list[0] - else: - call = warn.call_args_list[1] - error = call[0][1] - assert error == InsecurePlatformWarning + # If we're using a deprecated TLS version we can remove 'DeprecationWarning' + if self.tls_protocol_deprecated(): + w = [x for x in w if x.category != DeprecationWarning] + + # Modern versions of Python, or systems using PyOpenSSL, don't + # emit warnings. + if ( + sys.version_info >= (2, 7, 9) + or util.IS_PYOPENSSL + or util.IS_SECURETRANSPORT + ): + assert w == [] + else: + assert len(w) > 1 + assert any(x.category == InsecureRequestWarning for x in w) def test_verified_with_context(self): ctx = util.ssl_.create_urllib3_context(cert_reqs=ssl.CERT_REQUIRED) @@ -306,10 +308,15 @@ class TestHTTPS(HTTPSDummyServerTestCase): conn = https_pool._new_conn() assert conn.__class__ == VerifiedHTTPSConnection - with mock.patch("warnings.warn") as warn: + with warnings.catch_warnings(record=True) as w: r = https_pool.request("GET", "/") assert r.status == 200 - assert not warn.called, warn.call_args_list + + # If we're using a deprecated TLS version we can remove 'DeprecationWarning' + if self.tls_protocol_deprecated(): + w = [x for x in w if x.category != DeprecationWarning] + + assert w == [] def test_invalid_common_name(self): with HTTPSConnectionPool( @@ -391,6 +398,11 @@ class TestHTTPS(HTTPSDummyServerTestCase): # the unverified warning. Older systems may also emit other # warnings, which we want to ignore here. calls = warn.call_args_list + + # If we're using a deprecated TLS version we can remove 'DeprecationWarning' + if self.tls_protocol_deprecated(): + calls = [call for call in calls if call[0][1] != DeprecationWarning] + if ( sys.version_info >= (2, 7, 9) or util.IS_PYOPENSSL @@ -665,7 +677,13 @@ class TestHTTPS(HTTPSDummyServerTestCase): ) as https_pool: https_pool.request(method, url) - return [x for x in w if not isinstance(x.message, ResourceWarning)] + w = [x for x in w if not isinstance(x.message, ResourceWarning)] + + # If we're using a deprecated TLS version we can remove 'DeprecationWarning' + if self.tls_protocol_deprecated(): + w = [x for x in w if x.category != DeprecationWarning] + + return w def test_set_ssl_version_to_tls_version(self): if self.tls_protocol_name is None: @@ -699,6 +717,68 @@ class TestHTTPS(HTTPSDummyServerTestCase): finally: conn.close() + def test_default_tls_version_deprecations(self): + if self.tls_protocol_name is None: + pytest.skip("Skipping base test class") + + with HTTPSConnectionPool( + self.host, self.port, ca_certs=DEFAULT_CA + ) as https_pool: + conn = https_pool._get_conn() + try: + with warnings.catch_warnings(record=True) as w: + conn.connect() + if not hasattr(conn.sock, "version"): + pytest.skip("SSLSocket.version() not available") + finally: + conn.close() + + if self.tls_protocol_deprecated(): + assert len(w) == 1 + assert str(w[0].message) == ( + "Negotiating TLSv1/TLSv1.1 by default is deprecated " + "and will be disabled in urllib3 v2.0.0. Connecting to " + "'%s' with '%s' can be enabled by explicitly opting-in " + "with 'ssl_version'" % (self.host, self.tls_protocol_name) + ) + else: + assert w == [] + + def test_no_tls_version_deprecation_with_ssl_version(self): + if self.tls_protocol_name is None: + pytest.skip("Skipping base test class") + + with HTTPSConnectionPool( + self.host, self.port, ca_certs=DEFAULT_CA, ssl_version=util.PROTOCOL_TLS + ) as https_pool: + conn = https_pool._get_conn() + try: + with warnings.catch_warnings(record=True) as w: + conn.connect() + finally: + conn.close() + + assert w == [] + + def test_no_tls_version_deprecation_with_ssl_context(self): + if self.tls_protocol_name is None: + pytest.skip("Skipping base test class") + + with HTTPSConnectionPool( + self.host, + self.port, + ca_certs=DEFAULT_CA, + ssl_context=util.ssl_.create_urllib3_context(), + ) as https_pool: + conn = https_pool._get_conn() + try: + with warnings.catch_warnings(record=True) as w: + conn.connect() + finally: + conn.close() + + assert w == [] + @pytest.mark.skipif(sys.version_info < (3, 8), reason="requires python 3.8+") def test_sslkeylogfile(self, tmpdir, monkeypatch): if not hasattr(util.SSLContext, "keylog_filename"): |