Secure transports for DNS

Traditional DNS queries and replies are sent over UDP or TCP without encryption, making them subject to surveillance, spoofing, and DNS-based Internet filtering. Responses to clients from public resolvers like Google Public DNS are especially vulnerable to this, as messages may pass through many networks, while messages between recursive resolvers and authoritative name servers often incorporate additional protections.

To address these issues, in 2016 we launched DNS over HTTPS (now called DoH) offering encrypted DNSSEC-validating DNS resolution over HTTPS and QUIC. And in 2019, we added support for the DNS over TLS (DoT) standard used by the Android Private DNS feature.

DoH and DoT enhance privacy and security between clients and resolvers, complementing Google Public DNS validation of DNSSEC to provide end-to-end authenticated DNS for DNSSEC-signed domains. With Google Public DNS, we’re committed to providing fast, private, and secure DNS resolution for both DoH and DoT clients.

Supported TLS versions and crypto suites

Google Public DNS supports TLS 1.2 and TLS 1.3 for both DoH and DoT; no earlier versions of TLS or SSL are supported. Only cipher suites with forward security and Authenticated Encryption with Additional Data (AEAD) are supported. Qualys SSL Labs shows the current set of supported cipher suites.

Endpoints

Google Public DNS uses the following endpoints for DoH and DoT:

DoT (port 853) dns.google

DoH (port 443) URI templates

  • RFC 8484 – https://dns.google/dns-query{?dns}

    • For POST the URL is just https://dns.google/dns-query and the body of the HTTP request is the binary UDP DNS payload with content type application/dns-message.
    • For GET this is https://dns.google/dns-query?dns=BASE64URL_OF_QUERY.
  • JSON API – https://dns.google/resolve{?name}{&type,cd,do,…}

    • More GET parameters are described on the JSON API page. Only the name parameter is required.

Clients

There are a number of client applications that use DoT or DoH

  • Android 9 (Pie) “Private Browsing” feature – DoT
  • Intra (Android app) – DoH

The dnsprivacy.org website lists several other clients for DoT and DoH, but these typically require moderately technical configuration.

Command-line examples

The following command-line examples are not intended for use in an actual client and are merely illustrations using commonly available diagnostic tools.

DoT

The following commands require Knot DNS kdig 2.3.0 or later; with 2.7.4 or later, uncomment the +tls‑sni to send SNI as required by TLS 1.3.

kdig -d +noall +answer @dns.google example.com \
  +tls-ca +tls-hostname=dns.google # +tls-sni=dns.google
;; DEBUG: Querying for owner(example.com.), class(1), type(1), server(dns.google), port(853), protocol(TCP)
;; DEBUG: TLS, imported 312 system certificates
;; DEBUG: TLS, received certificate hierarchy:
;; DEBUG:  #1, C=US,ST=California,L=Mountain View,O=Google LLC,CN=dns.google
;; DEBUG:      SHA-256 PIN: lQXSLnWzUdueQ4+YCezIcLa8L6RPr8Wgeqtxmw1ti+M=
;; DEBUG:  #2, C=US,O=Google Trust Services,CN=Google Internet Authority G3
;; DEBUG:      SHA-256 PIN: f8NnEFZxQ4ExFOhSN7EiFWtiudZQVD2oY60uauV/n78=
;; DEBUG: TLS, skipping certificate PIN check
;; DEBUG: TLS, The certificate is trusted.

;; ANSWER SECTION:
example.com.            2046    IN      A       93.184.216.34
kdig -d +noall +answer @dns.google example.com \
  +tls-pin=f8NnEFZxQ4ExFOhSN7EiFWtiudZQVD2oY60uauV/n78= \
  # +tls-sni=dns.google
;; DEBUG: Querying for owner(example.com.), class(1), type(1), server(dns.google), port(853), protocol(TCP)
;; DEBUG: TLS, received certificate hierarchy:
;; DEBUG:  #1, C=US,ST=California,L=Mountain View,O=Google LLC,CN=dns.google
;; DEBUG:      SHA-256 PIN: lQXSLnWzUdueQ4+YCezIcLa8L6RPr8Wgeqtxmw1ti+M=
;; DEBUG:  #2, C=US,O=Google Trust Services,CN=Google Internet Authority G3
;; DEBUG:      SHA-256 PIN: f8NnEFZxQ4ExFOhSN7EiFWtiudZQVD2oY60uauV/n78=, MATCH
;; DEBUG: TLS, skipping certificate verification

;; ANSWER SECTION:
example.com.            5494    IN      A       93.184.216.34

DoH

RFC 8484 POST

The Base64Url encoded string in this command is the DNS message sent by dig +noedns example.test A with the DNS ID field set to zero, as recommended by RFC 8484 section 4.1. The shell command sends that DNS query as the binary data body content, using the Content-Type application/dns-message.

echo AAABAAABAAAAAAAAB2V4YW1wbGUEdGVzdAAAAQAB | base64 --decode |
 curl -is --data-binary @- -H 'content-type: application/dns-message' \
   https://dns.google/dns-query
HTTP/2 200
strict-transport-security: max-age=31536000; includeSubDomains; preload
access-control-allow-origin: *
date: Wed, 29 May 2019 19:37:16 GMT
expires: Wed, 29 May 2019 19:37:16 GMT
cache-control: private, max-age=19174
content-type: application/dns-message
server: HTTP server (unknown)
content-length: 45
x-xss-protection: 0
x-frame-options: SAMEORIGIN
alt-svc: quic=":443"; ma=2592000; v="46,44,43,39"

RFC 8484 GET

The Base64Url encoded string in this command is the DNS message sent by dig +noedns example.com A with the DNS ID field set to zero. In this case it is explicitly passed in the URL.

curl -i https://dns.google/dns-query?dns=AAABAAABAAAAAAAAB2V4YW1wbGUDY29tAAABAAE
HTTP/2 200
strict-transport-security: max-age=31536000; includeSubDomains; preload
access-control-allow-origin: *
date: Wed, 29 May 2019 19:37:16 GMT
expires: Wed, 29 May 2019 19:37:16 GMT
cache-control: private, max-age=19174
content-type: application/dns-message
server: HTTP server (unknown)
content-length: 45
x-xss-protection: 0
x-frame-options: SAMEORIGIN
alt-svc: quic=":443"; ma=2592000; v="46,44,43,39"

JSON GET

This uses the JSON API for DoH.

curl -i 'https://dns.google/resolve?name=example.com&type=a&do=1'
HTTP/2 200
strict-transport-security: max-age=31536000; includeSubDomains; preload
access-control-allow-origin: *
date: Thu, 30 May 2019 02:46:46 GMT
expires: Thu, 30 May 2019 02:46:46 GMT
cache-control: private, max-age=10443
content-type: application/x-javascript; charset=UTF-8
server: HTTP server (unknown)
x-xss-protection: 0
x-frame-options: SAMEORIGIN
alt-svc: quic=":443"; ma=2592000; v="46,44,43,39"
accept-ranges: none
vary: Accept-Encoding

{"Status": 0,"TC": false,"RD": true,"RA": true,"AD": true,"CD": false,"Question":[ {"name": "example.com.","type": 1}],"Answer":[ {"name": "example.com.","type": 1,"TTL": 10443,"data": "93.184.216.34"},{"name": "example.com.","type": 46,"TTL": 10443,"data": "a 8 2 86400 1559899303 1558087103 23689 example.com. IfelQcO5NqQIX7ZNKI245KLfdRCKBaj2gKhZkJawtJbo/do+A0aUvoDM5A7EZKcF/j8SdtyfYWj/8g91B2/m/WOo7KyZxIC918R1/jvBRYQGreDL+yutb1ReGc6eUHX+NKJIYqzfal+PY7tGotS1Srn9WhBspXq8/0rNsEnsSoA="}],"Additional":[]}

TLS 1.3 and SNI for IP address URLs

TLS 1.3 requires that clients provide Server Name Identification (SNI).

The SNI extension specifies that SNI information is a DNS domain (and not an IP address):

"HostName" contains the fully qualified DNS hostname of the server, as understood by the client. The hostname is represented as a byte string using ASCII encoding without a trailing dot. This allows the support of internationalized domain names through the use of A-labels defined in RFC5890. DNS hostnames are case-insensitive. The algorithm to compare hostnames is described in RFC5890, Section 2.3.2.4.

Literal IPv4 and IPv6 addresses are not permitted in "HostName".

These requirements can be hard to meet for DoH or DoT applications that want to take advantage of security improvements in TLS 1.3. Google Public DNS currently accepts TLS 1.3 connections that do not provide SNI, but we may need to change this for operational or security reasons in the future.

Our recommendations for DoT or DoH applications regarding SNI are the following:

  1. Send the dns.google hostname as SNI for any connections to the Google Public DNS DoT or DoH services.
  2. If no hostname is available (for example, in an application that is doing opportunistic DoT), it is better to send the IP address in the SNI rather than leave it blank.
  3. IPv6 addresses should appear in [2001:db8:1234::5678] bracketed form in the Host header, but without brackets in the SNI.

DNS Response Truncation

Although Google Public DNS generally does not truncate responses to DoT and DoH queries, there are two circumstances when it does:

  1. If Google Public DNS cannot get complete and un-truncated responses from authoritative name servers, it sets the TC flag in the response.

  2. In cases where the DNS response (in binary DNS message form) would exceed the 64 KiB limit for TCP DNS messages, Google Public DNS may set the TC (truncation) flag if RFC standards require it to do so.

However, in these cases, there is no need for clients to retry using plain TCP or any other transport, since the result will be the same.