From ef2795673b476726be138d514db23435cc842183 Mon Sep 17 00:00:00 2001 From: Maximilian Hils Date: Tue, 20 Jul 2021 15:23:25 +0200 Subject: [PATCH] disable HTTP/2 CONNECT for secure web proxies --- CHANGELOG.md | 6 ++++++ mitmproxy/addons/tlsconfig.py | 14 ++++++++++++-- mitmproxy/proxy/layers/tls.py | 17 +++++++++++++++++ 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b8826b6e..eb89f3197 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Release History +## Unreleased: mitmproxy next + +* Don't negotiate HTTP/2 on the outer TLS connection in Secure Web Proxy Mode. + This fixes compatibility with Firefox. + + ## 16 July 2021: mitmproxy 7.0 ### New Proxy Core (@mhils, [blog post](https://www.mitmproxy.org/posts/releases/mitmproxy7/)) diff --git a/mitmproxy/addons/tlsconfig.py b/mitmproxy/addons/tlsconfig.py index f514e5e0e..1f210d721 100644 --- a/mitmproxy/addons/tlsconfig.py +++ b/mitmproxy/addons/tlsconfig.py @@ -8,10 +8,11 @@ from mitmproxy import certs, ctx, exceptions, connection from mitmproxy.net import tls as net_tls from mitmproxy.options import CONF_BASENAME from mitmproxy.proxy import context -from mitmproxy.proxy.layers import tls +from mitmproxy.proxy.layers import tls, modes # We manually need to specify this, otherwise OpenSSL may select a non-HTTP2 cipher by default. # https://ssl-config.mozilla.org/#config=old + DEFAULT_CIPHERS = ( 'ECDHE-ECDSA-AES128-GCM-SHA256', 'ECDHE-RSA-AES128-GCM-SHA256', 'ECDHE-ECDSA-AES256-GCM-SHA384', 'ECDHE-RSA-AES256-GCM-SHA384', 'ECDHE-ECDSA-CHACHA20-POLY1305', 'ECDHE-RSA-CHACHA20-POLY1305', @@ -149,8 +150,17 @@ class TlsConfig: dhparams=self.certstore.dhparams, ) tls_start.ssl_conn = SSL.Connection(ssl_ctx) + + # Force HTTP/1 for secure web proxies, we currently don't support CONNECT over HTTP/2. + # There is a proof-of-concept branch at https://github.com/mhils/mitmproxy/tree/http2-proxy, + # but the complexity outweighs the benefits for now. + if len(tls_start.context.layers) == 2 and isinstance(tls_start.context.layers[0], modes.HttpProxy): + client_alpn: Optional[bytes] = b"http/1.1" + else: + client_alpn = client.alpn + tls_start.ssl_conn.set_app_data(AppData( - client_alpn=client.alpn, + client_alpn=client_alpn, server_alpn=server.alpn, http2=ctx.options.http2, )) diff --git a/mitmproxy/proxy/layers/tls.py b/mitmproxy/proxy/layers/tls.py index c9a2ff2b3..bb2cfd492 100644 --- a/mitmproxy/proxy/layers/tls.py +++ b/mitmproxy/proxy/layers/tls.py @@ -346,6 +346,23 @@ class ClientTLSLayer(_TLSLayer): client_hello_parsed: bool = False def __init__(self, context: context.Context): + if context.client.tls: + # In the case of TLS-over-TLS, we already have client TLS. As the outer TLS connection between client + # and proxy isn't that interesting to us, we just unset the attributes here and keep the inner TLS + # session's attributes. + # Alternatively we could create a new Client instance, + # but for now we keep it simple. There is a proof-of-concept at + # https://github.com/mitmproxy/mitmproxy/commit/9b6e2a716888b7787514733b76a5936afa485352. + context.client.alpn = None + context.client.cipher = None + context.client.sni = None + context.client.timestamp_tls_setup = None + context.client.tls_version = None + context.client.certificate_list = [] + context.client.mitmcert = None + context.client.alpn_offers = [] + context.client.cipher_list = [] + super().__init__(context, context.client) self.server_tls_available = isinstance(self.context.layers[-2], ServerTLSLayer) self.recv_buffer = bytearray()