diff --git a/changelog/2791.feature.rst b/changelog/2791.feature.rst new file mode 100644 index 0000000000..96c68784a1 --- /dev/null +++ b/changelog/2791.feature.rst @@ -0,0 +1 @@ +Emit ``InsecureProxyWarning`` when the proxy connection is not verified but the proxy.scheme is https \ No newline at end of file diff --git a/docs/advanced-usage.rst b/docs/advanced-usage.rst index 36a51e6725..129dd0fca2 100644 --- a/docs/advanced-usage.rst +++ b/docs/advanced-usage.rst @@ -509,6 +509,12 @@ be resolved in different ways. verification enabled. Follow the :ref:`certificate verification ` guide to resolve this warning. +* :class:`~exceptions.InsecureProxyWarning` + This happens when a request is made to an HTTPS Proxy without certificate + verification enabled or the proxy connection is not verified. + Follow the :ref:`certificate verification ` guide to + resolve this warning. + .. _disable_ssl_warnings: Making unverified HTTPS requests is **strongly** discouraged, however, if you diff --git a/src/urllib3/connectionpool.py b/src/urllib3/connectionpool.py index 52bb8657fc..372e7b61cc 100644 --- a/src/urllib3/connectionpool.py +++ b/src/urllib3/connectionpool.py @@ -29,6 +29,7 @@ EmptyPoolError, FullPoolError, HostChangedError, + InsecureProxyWarning, InsecureRequestWarning, LocationValueError, MaxRetryError, @@ -1098,11 +1099,21 @@ def _validate_conn(self, conn: BaseHTTPConnection) -> None: if conn.is_closed: conn.connect() - # TODO revise this, see https://github.com/urllib3/urllib3/issues/2791 + if conn.proxy and conn.proxy.scheme == "https" and not conn.proxy_is_verified: + warnings.warn( + ( + f"Unverified HTTPS request is being made using proxy host '{conn.proxy.host}'. " + "Adding certificate verification is strongly advised. See: " + "https://urllib3.readthedocs.io/en/latest/advanced-usage.html" + "#tls-warnings" + ), + InsecureProxyWarning, + ) + if not conn.is_verified and not conn.proxy_is_verified: warnings.warn( ( - f"Unverified HTTPS request is being made to host '{conn.host}'. " + f"Unverified HTTPS request is being made to host '{conn._tunnel_host if conn._tunnel_host else conn.host}'. " # type: ignore[attr-defined] "Adding certificate verification is strongly advised. See: " "https://urllib3.readthedocs.io/en/latest/advanced-usage.html" "#tls-warnings" diff --git a/src/urllib3/exceptions.py b/src/urllib3/exceptions.py index 5bb9236961..9816ac20e6 100644 --- a/src/urllib3/exceptions.py +++ b/src/urllib3/exceptions.py @@ -214,6 +214,10 @@ class InsecureRequestWarning(SecurityWarning): """Warned when making an unverified HTTPS request.""" +class InsecureProxyWarning(SecurityWarning): + """Warned when using an unverified proxy.""" + + class NotOpenSSLWarning(SecurityWarning): """Warned when using unsupported SSL library""" diff --git a/test/with_dummyserver/test_no_ssl.py b/test/with_dummyserver/test_no_ssl.py index 9a28119abf..96216ac5fa 100644 --- a/test/with_dummyserver/test_no_ssl.py +++ b/test/with_dummyserver/test_no_ssl.py @@ -9,10 +9,10 @@ import urllib3 from dummyserver.testcase import ( - HTTPSHypercornDummyServerTestCase, + HypercornDummyProxyTestCase, HypercornDummyServerTestCase, ) -from urllib3.exceptions import InsecureRequestWarning +from urllib3.exceptions import InsecureProxyWarning, InsecureRequestWarning from ..test_no_ssl import TestWithoutSSL @@ -24,13 +24,32 @@ def test_simple(self) -> None: assert r.status == 200, r.data -class TestHTTPSWithoutSSL(HTTPSHypercornDummyServerTestCase, TestWithoutSSL): +class TestHTTPSWithoutSSL(HypercornDummyProxyTestCase, TestWithoutSSL): def test_simple(self) -> None: with urllib3.HTTPSConnectionPool( - self.host, self.port, cert_reqs="NONE" + self.https_host, self.https_port, cert_reqs="NONE" ) as pool: with pytest.warns(InsecureRequestWarning): try: pool.request("GET", "/") except urllib3.exceptions.SSLError as e: assert "SSL module is not available" in str(e) + + def test_simple_proxy(self) -> None: + https_proxy_url = f"https://{self.proxy_host}:{int(self.https_proxy_port)}" + with urllib3.ProxyManager(https_proxy_url, cert_reqs="NONE") as pool: + with pytest.warns((InsecureProxyWarning, InsecureRequestWarning)) as record: + try: + pool.request("GET", "https://urllib3.readthedocs.io") + except urllib3.exceptions.SSLError as e: + assert "SSL module is not available" in str(e) + assert "localhost" in str(record[0].message) + assert "urllib3.readthedocs.io" in str(record[1].message) + + with pytest.warns((InsecureProxyWarning, InsecureRequestWarning)) as record: + try: + pool.request("GET", f"http://{self.http_host}:{self.http_port}") + except urllib3.exceptions.SSLError as e: + assert "SSL module is not available" in str(e) + assert "localhost" in str(record[0].message) + assert self.http_host in str(record[1].message)