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 blacklist
  • domains-whitelist.conf is used to configure sources for the whitelist
  • domains-blacklist-local-additions.txt is used to add domains to blacklist yourself. This file is not special, it is only included by default in domains-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

[3] Public blacklists