CNAME Uncloaking using dnscrypt-proxy
Advertisers are starting to encourage users to use CNAME to masquerade third-party trackers as first-party. Browser extensions do not get access to the DNS answer and are therefore unable to correctly block these trackers. It makes sense to find another way to block these trackers.
How does CNAME cloaking work?
CNAME is a way to map a domain to another one. This is usually used to create
aliases. However, trackers are now using CNAME to bypass third-party blocking.
It works by aliasing mysub.example.tld
to tracker.tld
. Browser-based
ad blockers only see mysub.example.tld
and hence are unable to block the
request made to the tracker.
While I do not endorse this behaviour, it may seem like a harmless cat-and-mouse game. It is not.
Why is it dangerous?
If you are not using an ad blocker, you might wonder what the difference is. In both cases, you would load the tracker and get the same kind of advertisement and tracking, right? … Wrong. By default, third-party websites are not given the same permissions. First-party trackers, on the other hand, often get access to cookies from the main domain. This means that unlike third-party, first-party trackers may access authentication cookie, session cookie, user preferences and a lot of other informations stored on the user browser.
For more information, take a look at this article [1].
How to block CNAME Cloaking using dnscrypt-proxy
To fix this, we are going to use dnscrypt-proxy
to implement the same kind of
blacklist NextDNS is doing [2].
Since yesterday, dnscrypt-proxy
is able to check for CNAME responses and match
it against the blacklist.
First, let’s enable the blacklist in /etc/dnscrypt-proxy/dnscrypt-proxy.toml
:
[blacklist]
blacklist_file = '/etc/dnscrypt-proxy/blacklist.txt'
Once it is done, we can begin blocking domains. Let’s try to see if it works.
Testing the blacklist is working
Find a domain that resolves with a CNAME. For instance f7ds.liberation.fr
.
Make sure the CNAME is correctly blacklisted.
Content of blacklist.txt
:
*.eulerian.net
$ dig f7ds.liberation.fr @9.9.9.9
;; ANSWER SECTION:
f7ds.liberation.fr. 3599 IN CNAME liberation.eulerian.net.
liberation.eulerian.net. 3599 IN CNAME atc.eulerian.net.
atc.eulerian.net. 3599 IN A 109.232.197.179
$ dig f7ds.liberation.fr @127.0.0.1
;; ANSWER SECTION:
f7ds.liberation.fr. 1 IN HINFO "This query has been locally blocked" "by dnscrypt-proxy"
Using public blacklists
Public blacklists [3] are made available on dnscrypt-proxy’s wiki. However, it is time-consuming to keep several of them up-to-date.
A script to download and aggregate them is also made available on dnscrypt-proxy’s
GitHub. It is able to read blocklist files from several format and output
a dnscrypt-proxy’s blacklist file.
This script is named generate-domains-blacklist.py
. In this article, I will
demonstrate how to use this script to download and aggregate several existing
blacklist into a single one.
To use it, you will need to clone dnscrypt-proxy’s git (or retrieve the script and configuration files).
$ git clone https://github.com/DNSCrypt/dnscrypt-proxy
$ cd dnscrypt-proxy/utils/generate-domains-blacklists
The script uses following files:
domains-blacklist.conf
is used to configure sources for the blacklistdomains-whitelist.conf
is used to configure sources for the whitelistdomains-blacklist-local-additions.txt
is used to add domains to blacklist yourself. This file is not special, it is only included by default indomains-blacklist.conf
domains-time-restricted.txt
is used to generate a list of time-restricted domains. This is not used for blocking trackers.
Configuration files names are not hard-coded so you can change the names to match your need. To do so, check the help part of the script.
For this article, only domains-blacklist.conf
will be used. This file is
composed of sources separated by newlines.
Several blocklist are enabled by default. These should be enough for the
average user and for this article.
Let’s try to generate a blacklist for our previous configuration:
To generate the blacklist from the given configuration files and output it on
our blacklist.txt
, simply use:
$ ./generate-domains-blacklist.py | sudo tee /etc/dnscrypt-proxy/blacklist.txt
$ systemctl restart dnscrypt-proxy
Dnscrypt-proxy needs to be restarted, otherwise the blacklist is not updated.
I would recommend updating frequently your blacklist. This can be done by creating a cron job that generate the blacklist file and restart the service.
Impact on performance
Unfortunately, this blacklist is definitely pricy. With the default settings, I get around 119000 lines in my blacklist. This adds an overhead of around 10-30 msec per non-cached DNS request on my laptop.
What’s next?
According to users on Hacker News, trackers are now asking websites to add an A or AAAA record instead of a CNAME. This means that it is going to get really hard to block them as IP based blacklist are not great. Moreover, this will continue exposing your cookies on misconfigured websites.
References
[1] CNAME Cloaking, the dangerous disguise of third-party trackers
[2] NextDNS first to support blocking of ALL third-party trackers disguised as first-party