Sep 8, 18
DNS over TLS - Thoughts and Implementation
Party like it's 1983
#networking #dns #privacyDiscussion on Hacker News.
A week or so I discovered that Android P has DNS over TLS support! It piqued my curiousity - could it finally be that DNS encryption goes mainstream?
In this post we’ll survey DNS over TLS, implement a client and share some thoughts!
TL;DR
Sure, with DNS over TLS your DNS queries are encrypted - that’s a major step forward! Nevertheless, the majority of the web relies on SNI (Server Name Indication), which sends the domain name in plaintext! (not for long?).
I built a DNS over TLS Node.js
package, install with $ npm i dns-over-tls
:
And a DNS over TLS command line tool, install with $ npm i -g dnstls
:
Why DNS over TLS?
DNS is insecure. It allows anyone with an active internet tap to view user queries and forge answers.
Over the years there’ve been many proposals on securing DNS. Below is a high level description of each, just so we’re on the same page.
- DNSCurve - proposed by Daniel J. Bernstein (@hashbreaker),
provides confidentiality (data transport is encrypted), integrity (data can’t be modified in an undetected manner) and
authenticity (the origin is verified to be who it claims to be)
between recursive DNS servers (e.g. Google’s
8.8.8.8
) and authoritative name servers (e.g.ns.ibm.com
). - DNSCrypt - based on DNSCurve, provides confidentiality and integrity
between stub resolvers (e.g. Android phone) and recursive DNS servers (e.g. Google’s
8.8.8.8
) - DNSSEC - provides authenticity and integrity, between resolvers (stub, recursive) and authoritative name servers. There’s a heated debate around DNSSEC, mainly because to achieve authentication, a new PKI (public-key infrastructure) was deployed, one which allows for partial government control of DNS (since some TLDs and ccTLDS are controlled by governments).
Bottom line, technicalities aside, the adoption of all lies somewhere between low to non-existent - leaving DNS a potent attack vector, ~35 years since it was created!
Moreover, evidence that DNS is actively tampered with is shown by a recent Usenix 2018 paper - Who Is Answering My Queries: Understanding and Characterizing Interception of the DNS Resolution Path.
Nevertheless, do not despair! Recently, adoption of both DNS over TLS (DoT) and DNS over HTTPS (DoH) has been gaining momentum, as evident by Android 9 support for DoT and Firefox Nightly support for DoH.
How does DNS over TLS Work?
Similarly to DNSCrypt, DNS over TLS encrypts the link between a stub resolver and a recursive resolver (1 and 8 below).
That is, it ensures no passive sniffing or active tampering of DNS can happen on that link (unless, of course, the underlying cryptography is somehow broken).
But what about the links 2 to 7?
Communication between recursive resolvers and other nameservers is likely to remain not private, but with the rising adoption of DNSSEC we can at least expect authenticity! (That is, passive sniffing on DNS requests from recursive resolvers would be possible, but active tampering won’t).
What if a rogue CA issues a valid certificate for the recursive resolver?
It’s possible for resourceful adversaries to obtain valid certificates for domains. For this reason, DNS over TLS’s RFC (RFC7858) introduces SPKI (Subject Public Key Info) Pinning:
Upon successful TLS connection and handshake, the client computes the SPKI Fingerprints for the public keys found in the validated server’s certificate chain (or in the raw public key, if the server provides that instead). If a computed fingerprint exactly matches one of the configured pins, the client continues with the connection as normal. Otherwise, the client MUST treat the SPKI validation failure as a non-recoverable error.
In simple words, when you get the details of your DNS over TLS resolver, such as IP address and hostname, you should also get an SPKI pin that your client can use to verify that this is the original server public key. For instance, check out the list of DNS over TLS servers at dnsprivacy.org - an SPKI pin is provided for each server.
The SPKI Pin (“fingerprint”) is the SHA256
of the public key part of the
recursive resolver’s X509
certificate. As an implementation reference, we can look on how
Android P calculates it.
Here’s an example that utilizes OpenSSL’s CLI to calculate the SPKI pin of Cloudflare’s DNS over TLS server:
$ echo | openssl s_client -connect '1.1.1.1:853' 2>/dev/null | openssl x509 -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64
Implementing a DNS over TLS Client
DNS over TLS simply tunnels good-old DNS packets over TLS with a default TCP port of 853
.
Once a TLS session is established, the client sends a regular DNS query and the server responds with a message that is prefixed with a two byte length field which gives the message length, excluding the two bytes length field.
Here’s a client implementation:
One thing that I haven’t implemented yet is SPKI pinning. Why? The SPKI Pin
requires the raw public key DER
of the server’s
certificate - which is only available for Node.js
9
and 10
. And while for
older Node.js
versions you can actually get the DER
of the entire certificate,
carving the public key DER
out of it is a delicate art, which I didn’t feel like doing :) (at least for now).
Playing with DNS over TLS
DNS over TLS is fairly new and there aren’t many tools for it -
for this reason I made dnstls
- a DNS over TLS command line tool:
It defaults to using Cloudflare’s DNS over TLS server (1.1.1.1
).
Install with:
$ npm i -g dnstls
Usage is dig
-like:
$ dnstls
Usage: dnstls name [type] [class] [@server] [-p<port>] [+tls-host=<host>]
Where:
name
is the domain you’d like to resolve.type
is the record type you’re after - e.g.A
(default),AAAA
,NS
,TXT
,MX
,CNAME
etc.class
is eitherIN
(default),HS
orCH
.port
is the TCP port, defaults to853.
server
the address of the server, defaults to1.1.1.1
.+tls-host
the TLS hostname that is noted in the server’s certificate, defaults tocloudflare-dns.com
.
Note: the server
and +tls-host
goes hand in hand - for instance, say
we’d like to use Quad9’s DNS over TLS server, we’d have to do:
$ dnstls @9.9.9.9 +tls-host=dns.quad9.net sagi.io
The output is a JSON
of the response (maybe dig
-like output in the future).
Example:
{
"id": 42209,
"type": "response",
"flags": 384,
"flag_qr": true,
"opcode": "QUERY",
"flag_aa": false,
"flag_tc": false,
"flag_rd": true,
"flag_ra": true,
"flag_z": false,
"flag_ad": false,
"flag_cd": false,
"rcode": "NOERROR",
"questions": [
{
"name": "sagi.io",
"type": "A",
"class": "IN"
}
],
"answers": [
{
"name": "sagi.io",
"type": "A",
"ttl": 101,
"class": "IN",
"flush": false,
"data": "151.101.65.195"
},
{
"name": "sagi.io",
"type": "A",
"ttl": 101,
"class": "IN",
"flush": false,
"data": "151.101.1.195"
}
],
"authorities": [],
"additionals": []
}
Thoughts
DNS over TLS is a major step forward towards a safer, more private, world. Nevertheless, here’re some thoughts:
Not a Commonly-used Default Port
I developed both library and command line client while working from Google
Campus Tel Aviv - which unfortunately blocks
all outgoing TCP connections that aren’t using commonly used destination ports
(such as 443
, for instance). I suspect that this is going to be an issue
for many networks.
Most Web DNS Traffic is Still Not Private
Sure, with DNS over TLS your DNS queries are encrypted - that’s a major step forward! Nevertheless, the majority of the web relies on SNI (Server Name Indication), which sends the domain name in plaintext! (not for long?).
Summary
I’d like to thank the folks at the DNS Privacy Project and the DPRIVE Working Group for their efforts! I think it finally starts to pay off :)
If you’re interested in DNS privacy, there’s a wealth of information in dnsprivacy.org and I invite you to watch NDSS 2018’s DNS Privacy Workshop sessions - very insightful.
If you spot an error / have any question please let me know so others may gain :)
Comments and thoughts are also welcome on this tweet:
DNS over TLS - Thoughts and Implementation #DNS #Privacy #NodeJS https://t.co/4OnlTu8WkQ
— Sagi Kedmi (@SagiKedmi) September 8, 2018