Block or allow ECH TLS connections
Encrypted Client Hello (ECH) is an extension to TLS that allows TLS to effectively hide information that is exposed in the unencrypted TLS ClientHello message. The ClientHello is one of the first messages sent in a TLS handshake, containing information inside the Server Name Indication (SNI) field about the destination host. By encrypting the ClientHello, this information is not exposed in plaintext.
Using the ECH extension, the client queries DNS for the DNS record of the destination host containing the ECH configuration, and public key for encrypting the ClientHello message. To prevent the identity from being exposed within the DNS query, clients usually use DoH or DoT to encrypt the DNS packets.
With the public key returned from the DNS server, the client can encrypt the ClientHello message, now called the inner ClientHello. An outer, unencrypted ClientHello must still be present to route to the server, often a CDN, that can unpack and reroute the traffic to the final destination.
Impact on the firewall
When a FortiGate does certificate inspection, for example for web category filtering, the FortiGate relies on the SNI field in the ClientHello to accurately determine the hostname of the server it is connecting to, and then performs category filtering based on this hostname. When ECH is used, this is equivalent to looking up the encrypted SNI field in the inner ClientHello. This means that applying ECH can break certificate inspection.
If the FortiGate is performing deep inspection, it always strips the ECH extension from an ECH, effectively forcing the client browser to use a non-ECH TLS connection. |
Prevent ECH from affecting certificate inspection
By preventing the use of ECH, the destination hostname is not encrypted and certificate inspection can be performed accurately on the SNI on the unencrypted ClientHello. There are two ways to do this:
-
Blocking the ClientHello that uses ECH, allowing TLS to fallback to using a plaintext ClientHello.
-
Stripping the ECH response that is returned from the DNS server, preventing TLS from receiving the ECH encryption key to encrypt the ClientHello.
Configuration
To configure blocking/allowing ECH and filtering on the SNI string in the TLS connection in the CLI:
config firewall ssl-ssh-profile edit <name> config https set status certificate-inspection set encrypted-client-hello {block | allow} end config ech-outer-sni edit <name> set sni <string> next end next end
encrypted-client-hello {block | allow} |
Block/allow session based on existence of encrypted-client-hello. |
ech-outer-sni |
Filtering on the ClientHelloOuter Server Name Indications (SNIs) to be blocked. |
sni <string> |
ClientHelloOuter SNI to be blocked. |
To configure stripping ECH information from DNS responses in the CLI:
config dnsfilter profile edit <name> set strip-ech {enable | disable} next end
strip-ech {enable | disable} |
Enable/disable removal of the ECH service parameter from supporting DNS RRs. |
To configure blocking/allowing ECH and filtering on the SNI string in the TLS connection GUI:
Blocking/allowing ECH is only configurable in an SSL/SSH inspection profile with Inspection method set to SSL Certificate Inspection. |
-
Go to Security Profiles > SSL/SSH Inspection and edit an existing profile or click Create New.
-
Set Inspection method to SSL Certificate Inspection.
-
Set Encrypted Client Hello to Block.
-
Click OK.
SNIs cannot be configured in the GUI.
To configure stripping ECH information from DNS responses in the GUI:
-
Go to Security Profiles > DNS Filter and edit an existing profile or click Create New.
-
Enable Strip Encrypted Client Hello service parameters.
-
Click OK.
Examples
Blocking TLS connections with certificate inspection when ECH is used in the TLS handshake through the FortiGate
In this example, an SSL/SSH inspection profile is configured to block TLS connections from some SNIs when ECH is used in the TLS handshake. Client messages with the outer SNI public.tls-ech.dev
are blocked.
A webfilter block message will be shown when trying to connect directly a public.tls-ech.dev. And accessing the webapge tls-ech.dev, which uses public.tls-ech.dev in its outer SNI, will show that the client is not using ECH.
To configure blocking a TLS connection that uses ECH:
-
Configure an SSL/SSH inspection profile to block ECH and set the SNIs to match the outer SNI in the ECH message during the TLS handshake:
config firewall ssl-ssh-profile edit "block-ech" config https set status certificate-inspection set encrypted-client-hello block end config ech-outer-sni edit "cloudflare" set sni "cloudflare-ech.com" next edit "tls-ech" set sni "public.tls-ech.dev" next edit "defo.ie" set sni "cover.defo.ie" next end next end
-
Apply the profile in a firewall policy:
config firewall policy edit 1 set ssl-ssh-profile "block-ech" next end
To check the results:
-
In a browser, enable DNS over HTTPS. For example, in Firefox go to Settings > Privacy & Security and under DNS over HTTPS enable Max Protection.
-
Visit a website, such as https://public.tls-ech.dev.
Because ECH is blocked and the outer SNI matches one of the configured SNIs, the ECH initiated by the browser is blocked. In this case, the browser shows a replacement message:
-
An SSL log shows the blocked connection:
2: date=2024-04-24 time=11:35:12 eventtime=1713983712769767807 tz="-0700" logid="1702062101" type="utm" subtype="ssl" eventtype="ssl-negotiation" level="warning" vd="vdom1" action="blocked" policyid=1 poluuid="52bd500a-01cb-51ef-2e99-70fcf0e1da3b" policytype="policy" sessionid=120849 service="HTTPS" profile="block-ech" srcip=10.1.100.143 srcport=63253 srccountry="Reserved" dstip=34.138.246.121 dstport=443 dstcountry="United States" srcintf="port1" srcintfrole="undefined" dstintf="port3" dstintfrole="undefined" srcuuid="fbd0def6-01ca-51ef-496f-08c90cb6b123" dstuuid="fbd0def6-01ca-51ef-496f-08c90cb6b123" proto=6 eventsubtype="encrypted-client-hello" hostname="public.tls-ech.dev" msg="SSL connection is blocked."
-
Try to visit a different website, such as tls-ech.dev, that does not match a default SNI to be blocked. The browser will initial try to establish an ECH-enabled TLS connection to public.tls-ech.dev, but that will be blocked, forcing the browser to connect to the actual website without ECH and, in this case, the browser will load the actual website and not the replacement message:
Allowing TLS connections with certificate inspection when ECH is used in the TLS handshake through the FortiGate
In this example, an SSL/SSH inspection profile is configured to allow TLS connections when ECH is used in the TLS handshake.
To configure allowing a TLS connection that uses ECH in the GUI:
-
Go to Security Profiles > SSL/SSH Inspection and edit an existing profile or click Create New.
-
Set Inspection method to SSL Certificate Inspection.
-
Set Encrypted Client Hello to Allow.
-
Click OK.
-
Go to Policy & Objects > Firewall Policy and edit an existing policy or click Create New.
-
Set SSL inspection to the SSL/SSH Inspection profile.
-
Click OK.
To configure allowing a TLS connection that uses ECH in the CLI:
-
Configure an SSL/SSH inspection profile to block ECH and set the SNIs to match the outer SNI in the ECH message during the TLS handshake:
config firewall ssl-ssh-profile edit "allow-ech" config https set status certificate-inspection set encrypted-client-hello allow end next end
-
Apply the profile in a firewall policy:
config firewall policy edit 1 set ssl-ssh-profile "allow-ech" next end
To check the results:
-
In a browser, enable DNS over HTTPS. For example, in Firefox go to Settings > Privacy & Security and under DNS over HTTPS enable Max Protection.
-
Visit a website, such as https://public.tls-ech.dev.
The ECH-enabled TLS connection is allowed through the firewall policy and the website opens:
-
An SSL log shows the allowed connection:
2: date=2024-04-24 time=11:13:31 eventtime=1713982410688781760 tz="-0700" logid="1702062103" type="utm" subtype="ssl" eventtype="ssl-negotiation" level="information" vd="vdom1" action="info" policyid=1 poluuid="52bd500a-01cb-51ef-2e99-70fcf0e1da3b" policytype="policy" sessionid=118528 service="HTTPS" profile="allow-ech" srcip=10.1.100.143 srcport=63232 srccountry="Reserved" dstip=34.138.246.121 dstport=443 dstcountry="United States" srcintf="port1" srcintfrole="undefined" dstintf="port3" dstintfrole="undefined" srcuuid="fbd0def6-01ca-51ef-496f-08c90cb6b123" dstuuid="fbd0def6-01ca-51ef-496f-08c90cb6b123" proto=6 eventsubtype="encrypted-client-hello" hostname="public.tls-ech.dev"
Stripping ECH information from DoH responses
DNS filters are used to strip ECH information from DNS responses, and force the browser to not use ECH for TLS connections. The browser relies on the ECH information from DNS over HTTPS (DoH) for ECH-enabled TLS connections.
In this example, a client sends a DoH query to a Cloudflare DNS server. The ECH informsation in the DNS response is stripped.
-
Policy 3 performs deep inspection on DoH traffic between the browser and the DoH server (mozilla.cloudflare-dns.com). A DNS filter profile is applied that strips the ECH information from the DoH response, forcing the browser to use a non-ECH TLS connection.
-
The browser then establishes a TLS connection through policy 1. Although policy 1 has an SSL/SSH inspection profile applied that allows ECH, ECH is not used because the ECH information from the DoH response was stripped by policy 3.
To configure and test stripping ECH information from DoH responses:
-
Configure the DNS filter profile that removes the ECH information:
config dnsfilter profile edit "strip-ech-enable" set strip-ech enable next end
-
Configure a firewall address:
config firewall address edit "mozilla.cloudflare-dns.com" set type fqdn set fqdn "mozilla.cloudflare-dns.com." next end
-
Configure the policies:
config firewall policy edit 3 set srcintf "port1" set dstintf "port3" set action accept set srcaddr "all" set dstaddr "mozilla.cloudflare-dns.com" set schedule "always" set service "ALL" set utm-status enable set inspection-mode proxy set ssl-ssh-profile "deep-inspection" set dnsfilter-profile "strip-ech-enable" set logtraffic all set nat enable next edit 1 set srcintf "port1" set dstintf "port3" set action accept set srcaddr "all" set dstaddr "all" set schedule "always" set service "ALL" set utm-status enable set inspection-mode proxy set ssl-ssh-profile "allow-ech" set logtraffic all set nat enable next end
-
In a browser, go to https://public.tls-ech.dev to see that ECH is not being used:
-
The debug log will show that the ECH service parameter was removed:
# diagnose debug application dnsproxy -1 ... [worker 0] dns_secure_filter_ech()-2166: Found ECH key=5 ... [worker 0] dns_secure_filter_ech()-2203: Removed ECH service parameter
-
If the DNS filter profile is changed to disable stripping the ECH information, then the website will show that ECH is being used:
config dnsfilter profile edit "strip-ech-enable" set strip-ech disable next end