RFC 9230
Oblivious DNS over HTTPS
Oblivious DNS over HTTPS ๐ฆ
okay so this is RFC 9230, "Oblivious DNS over HTTPS" ๐ธ It's an Independent Submission in the Experimental category (ISSN 2070-1721), dated June 2022. The authors are E. Kinnear (Apple Inc.), P. McManus (Fastly), T. Pauly (Apple Inc.), T. Verma, and C.A. Wood (Cloudflare). The title block doesn't list anything it obsoletes or updates โจ
Abstract
okay bestie, the big idea here is about hiding your IP address from DNS resolvers ๐ It does that by proxying encrypted DNS over HTTPS (DoH) messages. The privacy win is that no single server entity gets to know both your IP address and what your DNS queries and answers say ๐ฅ
This is an experimental protocol that was developed outside the IETF. It's published so people can implement it, make sure different implementations work together, and run experiments at a wide scale ๐งช
Status of This Memo
This document is not an Internet Standards Track specification ๐ It's published for examination, experimental implementation, and evaluation.
It defines an Experimental Protocol for the Internet community. It's a contribution to the RFC Series, separate from any other RFC stream. The RFC Editor chose to publish it at their discretion and makes no statement about its value for implementation or deployment. Documents the RFC Editor approves for publication are not candidates for any level of Internet Standard, see Section 2 of RFC 7841 ๐
For info about the current status, errata, and how to send feedback, check https://www.rfc-editor.org/info/rfc9230.
Copyright Notice
Copyright (c) 2022 IETF Trust and the persons identified as the document authors. All rights reserved ๐
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) as they stand on the date of publication. Please read these carefully, because they spell out your rights and restrictions for this document ๐
Table of Contents
1. Introduction
1.1. Specification of Requirements
2. Terminology
3. Deployment Requirements
4. HTTP Exchange
4.1. HTTP Request
4.2. HTTP Request Example
4.3. HTTP Response
4.4. HTTP Response Example
4.5. HTTP Metadata
5. Configuration and Public Key Format
6. Protocol Encoding
6.1. Message Format
6.2. Encryption and Decryption Routines
7. Oblivious Client Behavior
8. Oblivious Target Behavior
9. Compliance Requirements
10. Experiment Overview
11. Security Considerations
11.1. Denial of Service
11.2. Proxy Policies
11.3. Authentication
12. IANA Considerations
12.1. Oblivious DoH Message Media Type
13. References
13.1. Normative References
13.2. Informative References
Appendix A. Use of Generic Proxy Services
Acknowledgments
Authors' Addresses
1. Introduction ๐
okay so first, the background. DNS over HTTPS (DoH) [RFC8484] lets DNS messages travel inside HTTP messages that are protected with TLS. That gives DNS interactions better confidentiality and authentication in a bunch of different situations ๐
Here's the catch though ๐ญ DoH can stop eavesdroppers from reading DNS exchange contents directly, but clients still can't send queries to servers and get answers back without revealing their local IP address to that server. And that IP address can tell the server a lot about who you are or where you are ๐ซฃ
Proposals like Oblivious DNS [OBLIVIOUS-DNS] are the glow-up here ๐ฆ They make privacy better by making sure no single DNS server knows both the client IP address and the message contents.
This document defines Oblivious DoH, an experimental protocol built on DoH that allows proxied resolution. The DNS messages get encrypted so that no server can independently read both the client IP address and the DNS message contents ๐
Just like with DoH, DNS messages sent over Oblivious DoH are fully formed DNS messages. So if clients want answers that are relevant to the network they're on, without revealing their exact IP address, they can use the EDNS0 Client Subnet option ([RFC7871], Section 7.1.2) to give the resolver a hint over Oblivious DoH ๐
This mechanism is meant to be one of several ways to resolve privacy-sensitive content, as part of the bigger DNS privacy picture ๐ง
This experimental protocol was developed outside the IETF and is published so it can guide implementation, make sure implementations interoperate, and enable wide-scale experimentation. Check out Section 10 for more about the experiment โจ
1.1. Specification of Requirements
Here's how the requirement words work ๐ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be read as described in BCP 14 [RFC2119] [RFC8174], but only when they show up in all capitals, exactly as shown here. Lowercase versions are just regular words ๐
2. Terminology ๐
This document defines the following terms:
Oblivious Client: A client that sends DNS queries to an Oblivious Target, through an Oblivious Proxy. The Client is responsible for picking which combination of Proxy and Target to use for a given query ๐ง
Oblivious Proxy: An HTTP server that proxies encrypted DNS queries and responses between an Oblivious Client and an Oblivious Target. It's identified by a URI Template [RFC6570] (see Section 4.1). Note that the Oblivious Proxy isn't acting as a full HTTP proxy. It's a specialized server that only forwards Oblivious DNS messages ๐
Oblivious Target: An HTTP server that receives and decrypts encrypted Oblivious Client DNS queries from an Oblivious Proxy, then returns encrypted DNS responses through that same Proxy. To provide DNS responses, the Target can be a DNS resolver, be co-located with a resolver, or forward to a resolver ๐ฆ
Throughout the rest of the document, "Client", "Proxy", and "Target" are shorthand for Oblivious Client, Oblivious Proxy, and Oblivious Target ๐ซถ
3. Deployment Requirements ๐งฑ
Oblivious DoH needs, at a minimum:
- An Oblivious Proxy server, identified by a URI Template.
- An Oblivious Target server. The Target and Proxy are expected to be non-colluding (see Section 11). ๐
- One or more Target public keys for encrypting DNS queries sent to a Target via a Proxy (Section 5). These keys guarantee that only the intended Target can decrypt Client queries ๐
How you discover and provision the Proxy URI Template and the Target public keys is out of scope for this document ๐
4. HTTP Exchange ๐
Unlike direct resolution, oblivious hostname resolution over DoH involves three parties:
- The Client, which generates queries.
- The Proxy, which receives encrypted queries from the Client and passes them on to a Target.
- The Target, which receives proxied queries from the Client via the Proxy and produces proxied answers.
Here's the picture ๐ผ๏ธ
--- [ Request encrypted with Target public key ] -->
+---------+ +-----------+ +-----------+
| Client +-------------> Oblivious +-------------> Oblivious |
| <-------------+ Proxy <-------------+ Target |
+---------+ +-----------+ +-----------+
<-- [ Response encrypted with symmetric key ] ---
Figure 1: Oblivious DoH Exchange
4.1. HTTP Request ๐
Oblivious DoH queries get created by the Client and sent to the Proxy as HTTP requests using the POST method. Clients are configured with a Proxy URI Template [RFC6570] and the Target URI. The scheme for both the Proxy URI Template and the Target URI MUST be "https" ๐
The Proxy URI Template uses the Level 3 encoding defined in Section 1.2 of [RFC6570] and has two variables: "targethost", which is the hostname of the Target server, and "targetpath", which is the path where the Target can be reached. Here are a couple of example Proxy URI Templates ๐
https://dnsproxy.example/dns-query{?targethost,targetpath}
https://dnsproxy.example/{targethost}/{targetpath}
The URI Template MUST contain both the "targethost" and "targetpath" variables exactly once, and MUST NOT contain any other variables. The variables MUST be within the path or query components of the URI. Clients MUST ignore configurations that don't match this template ๐ โโ๏ธ Check out Section 4.2 for an example request ๐
Oblivious DoH messages have no cache value, because both requests and responses are encrypted with ephemeral key material. Requests and responses MUST NOT be cached ๐ซ๐๏ธ
Clients MUST set the HTTP Content-Type header to application/oblivious-dns-message to show that this request is an Oblivious DoH query meant for proxying. Clients also SHOULD set this same value for the HTTP Accept header ๐ฐ
So a correctly encoded request has the HTTP Content-Type header application/oblivious-dns-message, uses the HTTP POST method, and contains "targethost" and "targetpath" variables. If the Proxy fails to match the "targethost" and "targetpath" variables from the path, it MUST treat the request as malformed ๐ The Proxy builds the URI of the Target with the "https" scheme, using the value of "targethost" as the URI host and the percent-decoded value of "targetpath" as the URI path ๐งฉ
Proxies MUST check that Client requests are correctly encoded, and MUST return a 4xx (Client Error) if the check fails, along with the Proxy-Status response header with an "error" parameter of type "http_request_error" [RFC9209] โ ๏ธ
Heads up: Proxies MAY choose to not forward connections to non-standard ports. In that case, they can indicate the error with a 403 response status code, along with a Proxy-Status response header with an "error" parameter of type "http_request_denied" and an appropriate explanation in "details" ๐
If the Proxy can't connect to the Target, it can indicate the error with a 502 response status code, along with a Proxy-Status response header with an "error" parameter whose type says what went wrong. For example, if DNS resolution fails, the error type might be "dns_timeout", whereas if the TLS connection fails, it might be "tls_protocol_error" ๐ฅฒ
Last but not least, when Targets receive requests from a Proxy, they MUST validate that the request has the HTTP Content-Type header application/oblivious-dns-message and uses the HTTP POST method. Targets can respond with a 4xx response status code if this check fails ๐ซก
That's the end of part 1! ๐๐
4.2. HTTP Request Example โจ
okay so this example is giving main character energy ๐ A Client wants a Proxy called "dnsproxy.example" to pass an encrypted message along to "dnstarget.example". The URI Template for the Proxy is https://dnsproxy.example/dns-query{?targethost,targetpath}, and the URI for the Target is https://dnstarget.example/dns-query. ๐
:method = POST
:scheme = https
:authority = dnsproxy.example
:path = /dns-query?targethost=dnstarget.example&targetpath=/dns-query
accept = application/oblivious-dns-message
content-type = application/oblivious-dns-message
content-length = 106
<Bytes containing an encrypted Oblivious DNS query>
Then the Proxy sends this request on to the Target, like the good bestie it is ๐ซถ
:method = POST
:scheme = https
:authority = dnstarget.example
:path = /dns-query
accept = application/oblivious-dns-message
content-type = application/oblivious-dns-message
content-length = 106
<Bytes containing an encrypted Oblivious DNS query>
4.3. HTTP Response
okay so the Target is the one cooking up the response to an Oblivious DoH query ๐ฉโ๐ณ It MUST set the Content-Type HTTP header to application/oblivious-dns-message for all successful responses. The body holds an encrypted DNS message, so check Section 6 for the details ๐ฅ
The response from a Target MUST set the Content-Type HTTP header to application/oblivious-dns-message, and that exact same type MUST be used on all successful responses the Proxy sends to the Client. A Client MUST only consider a response that contains the Content-Type header before it processes the payload. A response without the right header MUST be treated as an error and handled appropriately. Everything else about HTTP responses and error handling is inherited from standard DoH ๐ธ
Proxies pass responses from the Target to the Client without changing the body or the status code. The Proxy also SHOULD add a Proxy-Status response header with a received-status parameter, which shows that the status code was generated by the Target โจ
Heads up, redirect bestie: if a Client gets a 3xx status code and decides to follow a redirect, the next request MUST also go through a Proxy, so requests are never directly exposed to the Target ๐
Requests the Target can't process result in 4xx (Client Error) responses. If the Target and Client keys don't match, that's an authorization failure (HTTP status code 401; see Section 15.5.2 of [HTTP]). Otherwise, if the Client's request is invalid, like a decryption failure, a wrong message type, or a deserialization failure, it's a bad request (HTTP status code 400; see Section 15.5.1 of [HTTP]) ๐
Even when the DNS response is a failure like SERVFAIL or NXDOMAIN, a successful HTTP response with a 2xx status code is used as long as the DNS response is valid. This matches how DoH [RFC8484] handles HTTP response codes, so it's giving consistency ๐ฆ
4.4. HTTP Response Example
This example shows a 2xx (Successful) response that a Target can send to a Client through a Proxy ๐ง
:status = 200
content-type = application/oblivious-dns-message
content-length = 154
<Bytes containing an encrypted Oblivious DNS response>
4.5. HTTP Metadata
Proxies forward requests and responses between Clients and Targets as described in Section 4.1. Metadata sent along with these messages could accidentally weaken or remove Oblivious DoH privacy properties ๐ญ Proxies MUST NOT send any Client-identifying information to Targets, such as "Forwarded" HTTP headers [RFC7239]. Clients MUST NOT include any private state in requests to Proxies either, such as HTTP cookies. Section 11.3 has more on Client authentication information, so go read it ๐ฅ
5. Configuration and Public Key Format
okay so to send a message to a Target, the Client needs a public key to encrypt its queries with ๐ How it finds that config is out of scope for this document, so that's a different mystery for another day ๐
Servers ought to rotate public keys regularly. It is RECOMMENDED that servers rotate keys every day ๐ Shorter rotation windows shrink the anonymity set of Clients that might use the public key, while longer rotation windows stretch out the time frame of possible compromise. So it's a balancing act, bestie โญ
An Oblivious DNS public key configuration is a structure encoded with TLS-style encoding [RFC8446], and it looks like this:
struct {
uint16 kem_id;
uint16 kdf_id;
uint16 aead_id;
opaque public_key<1..2^16-1>;
} ObliviousDoHConfigContents;
struct {
uint16 version;
uint16 length;
select (ObliviousDoHConfig.version) {
case 0x0001: ObliviousDoHConfigContents contents;
}
} ObliviousDoHConfig;
ObliviousDoHConfig ObliviousDoHConfigs<1..2^16-1>;
The ObliviousDoHConfigs structure holds one or more ObliviousDoHConfig structures, listed in decreasing order of preference. This lets a server support multiple versions of Oblivious DoH and multiple sets of Oblivious DoH parameters, which is so iconic of it ๐
An ObliviousDoHConfig structure holds a versioned representation of an Oblivious DoH configuration, with these fields:
version: The version of Oblivious DoH this configuration is used for. Clients MUST ignore any ObliviousDoHConfig structure with a version they do not support. The version of Oblivious DoH specified in this document is 0x0001.length: The length, in bytes, of the next field.contents: An opaque byte string whose contents depend on the version. For this specification, the contents are an ObliviousDoHConfigContents structure.
An ObliviousDoHConfigContents structure has the info needed to encrypt a message under ObliviousDoHConfigContents.public_key, so that only the owner of the matching private key can decrypt it ๐ The values for ObliviousDoHConfigContents.kem_id, ObliviousDoHConfigContents.kdf_id, and ObliviousDoHConfigContents.aead_id are described in Section 7 of [HPKE]. The fields work like this:
kem_id: The hybrid public key encryption (HPKE) key encapsulation mechanism (KEM) identifier corresponding topublic_key. Clients MUST ignore any ObliviousDoHConfig structure with a key using a KEM they do not support.kdf_id: The HPKE key derivation function (KDF) identifier corresponding topublic_key. Clients MUST ignore any ObliviousDoHConfig structure with a key using a KDF they do not support.aead_id: The HPKE authenticated encryption with associated data (AEAD) identifier corresponding topublic_key. Clients MUST ignore any ObliviousDoHConfig structure with a key using an AEAD they do not support.public_key: The HPKE public key the Client uses to encrypt Oblivious DoH queries ๐
6. Protocol Encoding
This section covers the encoding and wire format details for Oblivious DoH, plus the routines for encrypting and decrypting encoded values ๐ง
6.1. Message Format
There are two types of Oblivious DoH messages: Queries (0x01) and Responses (0x02). Both carry the following info:
- A DNS message, which is either a Query or a Response, depending on context.
- Padding of arbitrary length, which MUST contain all zeros.
They are encoded with this structure:
struct {
opaque dns_message<1..2^16-1>;
opaque padding<0..2^16-1>;
} ObliviousDoHMessagePlaintext;
Both Query and Response messages use the ObliviousDoHMessagePlaintext format:
ObliviousDoHMessagePlaintext ObliviousDoHQuery;
ObliviousDoHMessagePlaintext ObliviousDoHResponse;
An encrypted ObliviousDoHMessagePlaintext parameter gets carried in an ObliviousDoHMessage message, encoded like this:
struct {
uint8 message_type;
opaque key_id<0..2^16-1>;
opaque encrypted_message<1..2^16-1>;
} ObliviousDoHMessage;
The ObliviousDoHMessage structure has these fields:
message_type: A one-byte identifier for the type of message. Query messages use message_type 0x01, and Response messages use message_type 0x02.key_id: The identifier of the corresponding ObliviousDoHConfigContents key. It's computed asExpand(Extract("", config), "odoh key id", Nh), where config is the ObliviousDoHConfigContents structure, and Extract, Expand, and Nh are as specified by the HPKE cipher suite KDF corresponding toconfig.kdf_id.encrypted_message: An encrypted message for the Oblivious Target (for Query messages) or the Client (for Response messages). Implementations MAY enforce limits on the size of this field, depending on the size of plaintext DNS messages. (DNS queries, for example, won't hit the 2^16-1 size limit in practice ๐)
What ObliviousDoHMessage.encrypted_message contains depends on ObliviousDoHMessage.message_type. Specifically, ObliviousDoHMessage.encrypted_message is an encryption of an ObliviousDoHQuery message if the message is a Query, and an encryption of an ObliviousDoHResponse if the message is a Response ๐ฆ
6.2. Encryption and Decryption Routines
Clients use these utility functions to encrypt a Query and decrypt a Response, as described in Section 7 ๐
encrypt_query_body: Encrypts an Oblivious DoH query.
def encrypt_query_body(pkR, key_id, Q_plain):
enc, context = SetupBaseS(pkR, "odoh query")
aad = 0x01 || len(key_id) || key_id
ct = context.Seal(aad, Q_plain)
Q_encrypted = enc || ct
return Q_encrypted
decrypt_response_body: Decrypts an Oblivious DoH response.
def decrypt_response_body(context, Q_plain, R_encrypted, resp_nonce):
aead_key, aead_nonce = derive_secrets(context, Q_plain, resp_nonce)
aad = 0x02 || len(resp_nonce) || resp_nonce
R_plain, error = Open(key, nonce, aad, R_encrypted)
return R_plain, error
The derive_secrets function is described below.
Targets use these utility functions when processing queries and producing responses, as described in Section 8 ๐ฏ
setup_query_context: Sets up an HPKE context used for decrypting an Oblivious DoH query.
def setup_query_context(skR, key_id, Q_encrypted):
enc || ct = Q_encrypted
context = SetupBaseR(enc, skR, "odoh query")
return context
decrypt_query_body: Decrypts an Oblivious DoH query.
def decrypt_query_body(context, key_id, Q_encrypted):
aad = 0x01 || len(key_id) || key_id
enc || ct = Q_encrypted
Q_plain, error = context.Open(aad, ct)
return Q_plain, error
derive_secrets: Derives the keying material used for encrypting an Oblivious DoH response.
def derive_secrets(context, Q_plain, resp_nonce):
secret = context.Export("odoh response", Nk)
salt = Q_plain || len(resp_nonce) || resp_nonce
prk = Extract(salt, secret)
key = Expand(odoh_prk, "odoh key", Nk)
nonce = Expand(odoh_prk, "odoh nonce", Nn)
return key, nonce
The random(N) function returns N cryptographically secure random bytes from a good source of entropy [RFC4086]. The max(A, B) function returns A if A > B, and B otherwise ๐
encrypt_response_body: Encrypts an Oblivious DoH response.
def encrypt_response_body(R_plain, aead_key, aead_nonce, resp_nonce):
aad = 0x02 || len(resp_nonce) || resp_nonce
R_encrypted = Seal(aead_key, aead_nonce, aad, R_plain)
return R_encrypted
7. Oblivious Client Behavior โจ
okay so bestie, this is where the Client does its thing ๐
Say the Client wants to protect a DNS message (query) called M using Oblivious DoH ๐ฆ It's sending an Oblivious DoH Query for resolving M to an Oblivious Target, using an ObliviousDoHConfigContents config. Here's the whole glow-up sequence:
- It builds an
ObliviousDoHQuerystructure that carries the message M and padding, which gives usQ_plain๐ - It deserializes
config.public_keyinto a public keypkRof typeconfig.kem_id๐ - It computes the encrypted message as
Q_encrypted = encrypt_query_body(pkR, key_id, Q_plain), wherekey_idis computed as in Section 6. Also,len(key_id)outputs the length ofkey_idas a two-byte unsigned integer ๐งฎ - It outputs an
ObliviousDoHMessagemessageQ, whereQ.message_type = 0x01,Q.key_idcarrieskey_id, andQ.encrypted_message = Q_encrypted๐
Then the Client sends Q to the Proxy according to Section 4.1 ๐ Once it gets a response R, encrypted the way Section 8 describes, it uses decrypt_response_body to decrypt R.encrypted_message (using R.key_id as a nonce) and gets R_plain โจ
Clients MUST validateR_plain.padding(as all zeros) before usingR_plain.dns_message. No skipping this step, bestie ๐
8. Oblivious Target Behavior ๐ฏ
Targets that receive a Query message Q decrypt and process it like this:
- They look up the
ObliviousDoHConfigContentsinformation usingQ.key_id. If no such key exists, the Target MAY discard the query, and if it does, it MUST return a 401 (Unauthorized) response to the Proxy. Otherwise,skRis the private key that corresponds to this public key, or one picked for trial decryption ๐ - It computes
context = setup_query_context(skR, Q.key_id, Q.encrypted_message)๐ง - It computes
Q_plain, error = decrypt_query_body(context, Q.key_id, Q.encrypted_message)๐ - If no error came back and
Q_plain.paddingis valid (all zeros), it resolvesQ_plain.dns_messageas needed, yielding a DNS message M. Otherwise, if there was an error or the padding was invalid, it returns a 400 (Client Error) response to the Proxy ๐ โโ๏ธ - It creates an
ObliviousDoHResponseBodystructure that carries the message M and padding, which gives usR_plain๐ - It creates a fresh nonce
resp_nonce = random(max(Nn, Nk))๐ฒ - It computes
aead_key, aead_nonce = derive_secrets(context, Q_plain, resp_nonce)๐๏ธ - It computes
R_encrypted = encrypt_response_body(R_plain, aead_key, aead_nonce, resp_nonce). Thekey_idfield used for encryption carriesresp_nonce, so Clients can derive the same secrets. Also, the Seal function is the one associated with the HPKE AEAD ๐ธ - It outputs an
ObliviousDoHMessagemessageR, whereR.message_type = 0x02,R.key_id = resp_nonce, andR.encrypted_message = R_encrypted๐ฆ
The Target then sends R in a 2xx (Successful) response to the Proxy; see Section 4.3. The Proxy forwards the message R back to the Client without modification, as the HTTP response to the Client's original HTTP request ๐ If there's an error (a non-2xx status code), the Proxy forwards the Target error to the Client; see Section 4.3 too ๐ญ
9. Compliance Requirements ๐
Oblivious DoH uses HPKE for public key encryption [HPKE]. Unless an application profile standard says otherwise, a compliant Oblivious DoH implementation MUST support this HPKE cipher suite:
- KEM:
DHKEM(X25519, HKDF-SHA256)(see [HPKE], Section 7.1) - KDF:
HKDF-SHA256(see [HPKE], Section 7.2) - AEAD:
AES-128-GCM(see [HPKE], Section 7.3)
10. Experiment Overview ๐งช
This document describes an experimental protocol built on DoH, and the point of the experiment is to check whether a deployment configuration is viable and what performance impacts show up in DNS resolution. The team is measuring key performance indicators like resolution latency ๐ Experiment participants will test different parameters that affect service operation and performance, including the mechanisms for discovering and configuring DoH Proxies and Targets, plus the performance implications of connection reuse and pools where that makes sense ๐ The results will be used to shape future protocol design and deployment work for Oblivious DoH, like Oblivious HTTP [OHTP] ๐
Implementations of DoH that aren't part of the experiment won't recognize this protocol, so they won't take part in it ๐ โโ๏ธ It's expected that Oblivious DoH will be used widely, and that this experiment will run for a long time โณ
11. Security Considerations ๐
Okay, this section is serious, bestie ๐ญ Oblivious DoH aims to make sure that only Clients know the true query origin and what the query contains. As a simplified model, picture two Clients C1 and C2, one Proxy P, and one Target T. Oblivious DoH assumes an extended Dolev-Yao style attacker [Dolev-Yao] who can observe all network activity and can adaptively compromise either P or T, but not C1 or C2 ๐ Compromising both P and T is basically the same as those two colluding in practice ๐ค Once compromised, the attacker has access to all session information and private key material. (This generalizes to arbitrarily many Clients, Proxies, and Targets, with two constraints: (1) not all Targets and Proxies are compromised at the same time, and (2) at least two Clients are left uncompromised.) The attacker is not allowed to send Client-identifying information, such as IP addresses, to Targets. (That would let the attacker trivially link a query to its Client ๐จ)
In this model, C1 and C2 both send Oblivious DoH queries, Q1 and Q2 respectively, through P to T, and T returns answers A1 and A2. The attacker is trying to link C1 to (Q1, A1) and C2 to (Q2, A2). The attacker wins if that linking is possible without any additional interaction. (For example, if T is compromised, it could return a DNS answer pointing to an entity it controls, then watch the connection that follows from a Client and learn who that Client is in the process. Those attacks are out of scope for this model ๐)
Oblivious DoH security stops this kind of linkability ๐ Informally, that means:
- Queries and answers are known only to Clients and Targets that hold the matching response key and HPKE keying material. In particular, Proxies know the origin and destination of an oblivious query, but they don't know the plaintext query. Likewise, Targets only know the oblivious query origin, meaning the Proxy, and the plaintext query. Only the Client knows both the plaintext query contents and the destination โจ
- Target resolvers can't link queries from the same Client unless they have unique per-Client keys ๐ฆ
Traffic analysis mitigations are out of scope for this document. In particular, this document doesn't set padding lengths for ObliviousDoHQuery and ObliviousDoHResponse messages. Implementations SHOULD follow the guidance in [RFC8467] when choosing padding length ๐
Oblivious DoH security doesn't depend on Proxies and Targets being indistinguishable from each other. Specifically, an on-path attacker could tell whether a connection to a particular endpoint is being used for oblivious or direct DoH queries. However, that has no effect on the confidentiality goals listed above ๐ธ
11.1. Denial of Service ๐ซ
Malicious Clients (or Proxies) can send bogus Oblivious DoH queries to Targets as a Denial-of-Service (DoS) attack ๐ค Target servers can throttle request processing if this happens. Also, since Targets give explicit errors when decryption fails (that is, if ciphertext decryption fails or if the plaintext DNS message is malformed), Proxies can throttle specific Clients in response to those errors ๐ In general though, Targets trust Proxies not to overwhelm the Target, and Proxies are expected to implement some form of rate limiting or client authentication to limit abuse; see Section 11.3 ๐ก๏ธ
Malicious Targets or Proxies can send bogus answers in response to Oblivious DoH queries. A response decryption failure is a signal that either the Proxy or the Target is misbehaving ๐ Clients can choose to stop using one or both of those servers when this kind of failure happens. But, as noted above, malicious Targets and Proxies are out of scope for the threat model ๐
11.2. Proxy Policies ๐ช
Proxies are free to enforce any forwarding policy they want for Clients. For example, they can choose to forward requests only to known or otherwise trusted Targets ๐ง
Proxies that don't reuse connections to Targets across many Clients may let Targets link individual queries to unknown Targets ๐ฌ To close off this linkability vector, it is RECOMMENDED that Proxies pool and reuse connections to Targets ๐ซถ This helps performance too, not just privacy, since queries don't get delayed by Proxy-to-Target connection establishment โก
11.3. Authentication ๐
Depending on the deployment scenario, Proxies and Targets might need to authenticate before use. No matter which authentication mechanism is in place, Proxies MUST NOT reveal any Client authentication information to Targets. This is required so Targets can't uniquely identify individual Clients ๐ โโ๏ธ
Note that if Targets require Proxies to authenticate at the HTTP or application layer before use, this ought to happen before any Client query is forwarded to the Target. That lets Proxies tell apart a 401 (Unauthorized) response caused by authentication failure from a 401 response caused by a Client key mismatch; see Section 4.3 ๐งฉ
12. IANA Considerations ๐
This document changes the "Media Types" registry. The changes are described in the subsection below ๐
12.1. Oblivious DoH Message Media Type ๐
This document registers a new media type, application/oblivious-dns-message. Here's the registration template, verbatim:
Type name: application
Subtype name: oblivious-dns-message
Required parameters: N/A
Optional parameters: N/A
Encoding considerations: This is a binary format, containing
encrypted DNS requests and responses encoded as
ObliviousDoHMessage values, as defined in Section 6.1.
Security considerations: See this document. The content is an
encrypted DNS message, and not executable code.
Interoperability considerations: This document specifies the format
of conforming messages and the interpretation thereof; see
Section 6.1.
Published specification: This document
Applications that use this media type: This media type is intended
to be used by Clients wishing to hide their DNS queries when using
DNS over HTTPS.
Additional information: N/A
Person and email address to contact for further information: See the
Authors' Addresses section.
Intended usage: COMMON
Restrictions on usage: N/A
Author: Tommy Pauly (tpauly@apple.com)
Change controller: IETF
Provisional registration? (standards tree only): No
13. References ๐
13.1. Normative References ๐โจ
Okay bestie, these are the big-deal docs this spec leans on, so here's the whole list ๐
[HPKE] Barnes, R., Bhargavan, K., Lipp, B., and C. Wood, "Hybrid
Public Key Encryption", RFC 9180, DOI 10.17487/RFC9180,
February 2022, <https://www.rfc-editor.org/info/rfc9180>.
[HTTP] Fielding, R., Ed., Nottingham, M., Ed., and J. Reschke,
Ed., "HTTP Semantics", STD 97, RFC 9110,
DOI 10.17487/RFC9110, June 2022,
<https://www.rfc-editor.org/info/rfc9110>.
[RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
Requirement Levels", BCP 14, RFC 2119,
DOI 10.17487/RFC2119, March 1997,
<https://www.rfc-editor.org/info/rfc2119>.
[RFC4086] Eastlake 3rd, D., Schiller, J., and S. Crocker,
"Randomness Requirements for Security", BCP 106, RFC 4086,
DOI 10.17487/RFC4086, June 2005,
<https://www.rfc-editor.org/info/rfc4086>.
[RFC6570] Gregorio, J., Fielding, R., Hadley, M., Nottingham, M.,
and D. Orchard, "URI Template", RFC 6570,
DOI 10.17487/RFC6570, March 2012,
<https://www.rfc-editor.org/info/rfc6570>.
[RFC8174] Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC
2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174,
May 2017, <https://www.rfc-editor.org/info/rfc8174>.
[RFC8446] Rescorla, E., "The Transport Layer Security (TLS) Protocol
Version 1.3", RFC 8446, DOI 10.17487/RFC8446, August 2018,
<https://www.rfc-editor.org/info/rfc8446>.
[RFC8467] Mayrhofer, A., "Padding Policies for Extension Mechanisms
for DNS (EDNS(0))", RFC 8467, DOI 10.17487/RFC8467,
October 2018, <https://www.rfc-editor.org/info/rfc8467>.
[RFC8484] Hoffman, P. and P. McManus, "DNS Queries over HTTPS
(DoH)", RFC 8484, DOI 10.17487/RFC8484, October 2018,
<https://www.rfc-editor.org/info/rfc8484>.
[RFC9209] Nottingham, M. and P. Sikora, "The Proxy-Status HTTP
Response Header Field", RFC 9209, DOI 10.17487/RFC9209,
June 2022, <https://www.rfc-editor.org/info/rfc9209>.
13.2. Informative References ๐ฆ
These ones are the helpful reading list, the extra context that's nice to have ๐ธ
[Dolev-Yao]
Dolev, D. and A. C. Yao, "On the Security of Public Key
Protocols", IEEE Transactions on Information Theory, Vol.
IT-29, No. 2, DOI 10.1109/TIT.1983.1056650, March 1983,
<https://www.cs.huji.ac.il/~dolev/pubs/dolev-yao-ieee-
01056650.pdf>.
[OBLIVIOUS-DNS]
Edmundson, A., Schmitt, P., Feamster, N., and A. Mankin,
"Oblivious DNS - Strong Privacy for DNS Queries", Work in
Progress, Internet-Draft, draft-annee-dprive-oblivious-
dns-00, 2 July 2018,
<https://datatracker.ietf.org/doc/html/draft-annee-dprive-
oblivious-dns-00>.
[OHTP] Thomson, M. and C.A. Wood, "Oblivious HTTP", Work in
Progress, Internet-Draft, draft-ietf-ohai-ohttp-01, 15
February 2022, <https://datatracker.ietf.org/doc/html/
draft-ietf-ohai-ohttp-01>.
[RFC7239] Petersson, A. and M. Nilsson, "Forwarded HTTP Extension",
RFC 7239, DOI 10.17487/RFC7239, June 2014,
<https://www.rfc-editor.org/info/rfc7239>.
[RFC7871] Contavalli, C., van der Gaast, W., Lawrence, D., and W.
Kumari, "Client Subnet in DNS Queries", RFC 7871,
DOI 10.17487/RFC7871, May 2016,
<https://www.rfc-editor.org/info/rfc7871>.
Appendix A. Use of Generic Proxy Services ๐ง
Okay so, using DoH over anonymizing proxy services like Tor can also get you the goal of keeping query origins separate from their contents ๐ But there are a couple of reasons it's kinda less iconic than Oblivious DoH:
- Tor is meant to be a generic connection-level anonymity system. It has higher latency costs and protocol complexity just to proxy individual DNS queries ๐ฉ Oblivious DoH, on the other hand, is a lightweight protocol built on DoH. It's implemented as an application-layer proxy, and it can be turned on as a default mode for users who want extra privacy ๐
- As a one-hop proxy, Oblivious DoH encourages connectionless proxies to mitigate Client query correlation with few round trips โจ Multi-hop systems like Tor often run secure connections (TLS) end to end, which means DoH servers could track queries over the same connection ๐ Using a fresh DoH connection per query would cost a non-negligible penalty in connection setup time. That's a lot of wasted vibes ๐
Acknowledgments ๐ซถ
This work is inspired by Oblivious DNS [OBLIVIOUS-DNS]. Huge thanks to all of the authors of that document ๐ Thanks also to Nafeez Ahamed, Elliot Briggs, Marwan Fayed, Jonathan Hoyland, Frederic Jacobs, Tommy Jensen, Erik Nygren, Paul Schmitt, Brian Swander, and Peter Wu for their feedback and input. Literally the best squad ๐
Authors' Addresses ๐
Eric Kinnear
Apple Inc.
One Apple Park Way
Cupertino, California 95014
United States of America
Email: ekinnear@apple.com
Patrick McManus
Fastly
Email: mcmanus@ducksong.com
Tommy Pauly
Apple Inc.
One Apple Park Way
Cupertino, California 95014
United States of America
Email: tpauly@apple.com
Tanya Verma
Cloudflare
101 Townsend St
San Francisco, California 94107
United States of America
Email: vermatanyax@gmail.com
Christopher A. Wood
Cloudflare
101 Townsend St
San Francisco, California 94107
United States of America
Email: caw@heapingbits.net
That's the whole spec, bestie! ๐๐ โจ
๐ฆ where this page comes from
- ietf: rfc9230 2022-06
- ๐ girlified by claude-haiku-5-5 on : a language model's rewrite of the original, under its terms