Report on the TLS fingerprint of meek with ESNI

David Fifield

Summary

The following is an evaluation of the TLS fingerprint of meek, adapted to use ESNI in place of domain fronting. It compares three TLS fingerprints:

  1. Firefox without ESNI
  2. Firefox with ESNI
  3. meek through Firefox with ESNI

The fingerprints produced by (2) and (3) are the same. They differ from (1) in the lack of a server_name (plaintext SNI) extension, the possible omission of a padding extension, and the addition of an encrypted_server_name (encrypted SNI) extension when the server is identified by a name rather than an IP address.

When Firefox is configured to use ESNI, with name resolution provided by a DNS-over-HTTPS server identified by IP address, it actually produces two distinct TLS fingerprints: one for contacting the DNS-over-HTTPS server (identified by IP address), and one for contacting the web server (identified by hostname).

Test procedure

All tests use an instance of meek-server running behind Cloudflare at the domain name meek.rinsed-tinsel.site.

The tests use Firefox Developer Edition 67.0b6 linux-x86_64/en-US (download link) and Tor 0.2.9.16. The browser is set up with two profiles: "plain" and "esni" (run firefox --ProfileManager to create profiles). Both profiles have the following preferences set in a user.js file:

user_pref("app.normandy.enabled", false);
user_pref("app.update.auto", false);
user_pref("app.update.checkInstallTime", false);
user_pref("app.update.interval", 999999999);
user_pref("browser.laterrun.enabled", false);
user_pref("browser.newtabpage.activity-stream.disableSnippets", true);
user_pref("browser.newtabpage.activity-stream.discoverystream.endpoints", "");
user_pref("browser.newtabpage.activity-stream.enabled", false);
user_pref("browser.newtabpage.activity-stream.feeds.section.highlights", false);
user_pref("browser.newtabpage.activity-stream.feeds.section.topstories", false);
user_pref("browser.newtabpage.activity-stream.feeds.snippets", false);
user_pref("browser.newtabpage.activity-stream.feeds.topsites", false);
user_pref("browser.newtabpage.enabled", false);
user_pref("browser.safebrowsing.blockedURIs.enabled", false);
user_pref("browser.safebrowsing.downloads.enabled", false);
user_pref("browser.safebrowsing.malware.enabled", false);
user_pref("browser.safebrowsing.passwords.enabled", false);
user_pref("browser.safebrowsing.phishing.enabled", false);
user_pref("browser.safebrowsing.provider.mozilla.gethashURL", "");
user_pref("browser.safebrowsing.provider.mozilla.updateURL", "");
user_pref("browser.search.geoip.url", "");
user_pref("browser.search.geoSpecificDefaults", false);
user_pref("browser.search.suggest.enabled", false);
user_pref("browser.shell.checkDefaultBrowser", false);
user_pref("browser.startup.firstrunSkipsHomepage", false);
user_pref("browser.startup.homepage", "about:blank");
user_pref("datareporting.healthreport.uploadEnabled", false);
user_pref("datareporting.policy.dataSubmissionEnabled", false);
user_pref("dom.push.enabled", false);
user_pref("extensions.pocket.enabled", false);
user_pref("geo.enabled", false);
user_pref("geo.wifi.url", "");
user_pref("network.captive-portal-service.enabled", false);
user_pref("privacy.trackingprotection.enabled", false);
user_pref("privacy.trackingprotection.pbmode.enabled", false);
user_pref("startup.homepage_override_url", "");
user_pref("startup.homepage_welcome_url", "");
user_pref("toolkit.telemetry.enabled", false);
Preferences shared by the plain and esni browser profiles. These preferences disable background network traffic unrelated to user browsering actions.

The esni profile additionally has the following preferences set in user.js:

user_pref("browser.dom.window.dump.enabled", true);
user_pref("network.trr.mode", 3);
user_pref("network.trr.uri", "https://1.1.1.1/dns-query");
user_pref("network.security.esni.enabled", true);
user_pref("security.OCSP.enabled", 0);
user_pref("xpinstall.signatures.required", false);
Preferences shared by the plain and esni browser profiles. These enable ESNI and prepare the environment to run the meek-http-helper extension.

The esni profile then has the meek-http-helper extension installed according to these instructions. The version of meek is commit 5b96265e01cf5e82b9e82f9e9e05441ac5311245.

Packet captures are run in a separate Linux network namespace, in order to isolate the test's traffic from other traffic on the host. The network namespace is set up as follows:

# mkdir -p /etc/netns/net0
# sh -c 'echo nameserver 8.8.8.8 > /etc/netns/net0/resolv.conf'
# ip netns add net0
# ip link add veth-a type veth peer name veth-b
# ip link set veth-a netns net0
# ip netns exec net0 ip address add 127.0.0.1/8 dev lo
# ip netns exec net0 ip link set lo up
# ip netns exec net0 ip address add 192.168.100.2/24 dev veth-a
# ip netns exec net0 ip link set veth-a up
# ip address add 192.168.100.1/24 dev veth-b
# ip link set veth-b up
# ip netns exec net0 ip route add default via 192.168.100.1 dev veth-a
# sh -c 'echo 1 > /proc/sys/net/ipv4/ip_forward'
# iptables -t nat -A POSTROUTING -s 192.168.100.0/24 -o $INTERFACE -j SNAT --to-source $ADDRESS
Commands for setting up an isolated network namespace. $INTERFACE stands for the name of the default extenal network interface (e.g. eth0) and $ADDRESS stands for the interface's address (e.g. 192.168.0.2).

tor is configured with a torrc.esni file that refers to the meek-http-helper extension's listening address. The port number changes on each run of Firefox and has to be manually edited.

DataDirectory datadir
SOCKSPort auto
UseBridges 1
Bridge meek 0.0.2.0:3 url=https://meek.rinsed-tinsel.site/
ClientTransportPlugin meek exec ./meek-client --helper 127.0.0.1:$PORT
torrc.esni file. The $PORT must be edited as it changes on every run of Firefox.

Firefox and tor are run as follows, where $USER stands for the username of an ordinary user on the host:

# ip netns exec net0 tcpdump -U -w OUTPUT.PCAP
Runs a tcpdump packet capture.
# ip netns exec net0 su $USER -c './firefox -P plain'
Runs the plain Firefox profile.
# ip netns exec net0 su $USER -c './firefox -P esni'
Runs the esni Firefox profile.
# ip netns exec net0 su $USER -c 'tor -f torrc.esni'
Runs tor.

tor was run twice, as the first run would always fail due to an apparent Firefox bug.

Results

Firefox without ESNI

plain.pcap

Browsing directly to https://meek.rinsed-tinself.site/ results in TLS fingerprint 6bfedc5d5c740d58 (archive), which at the time of writing is ranked #11 all time and #7 in the past week.

The name is resolved with plaintext DNS and the server name appears in plaintext SNI.

Firefox with ESNI

esni.pcap

Browsing directly to https://meek.rinsed-tinself.site/ results in two TLS fingerprints.

The first fingerprint is 8300bf0e26f2a109 (archive) which ranks #3758 all time and #3643 in the past week. This fingerprint appears in the requests to 1.1.1.1, which is the DNS-over-HTTPS server configured. It differs (archive) from 6bfedc5d5c740d58 only in the lack of a server_name extension, which is expected because the server is identified by IP address.

The second fingerprint is 2dcbeba533890640 (archive) which ranks #6219 all time and #3681 in the past week. This fingerprint appears in the requests to meek.rinsed-tinsel.site. It differs (archive) from 6bfedc5d5c740d58 in that it lacks server_name and padding extensions, and gains a encrypted_server_name (0xffce) extension.

meek with ESNI

meek-esni.pcap

Bootstrapping tor with meek-client through the ESNI-enabled firefox results in the same two TLS fingerprints as in the Firefox with ESNI case.

A note on OCSP

OCSP must be disabled with using ESNI, because OCSP requests may leak the server name. That is why the esni browser profile has the `security.OCSP.enabled` pref set. Because the plain browser profile does not have this pref set, I expected to see OCSP requests regarding meek.rinsed-tinself.site in plain.pcap, but did not, for unknown reasons.