Le concept du DOH, DNS Over HTTPS, est simple : faire causer un serveur DNS à travers le protocole HTTP.
Le protocole classique du DNS utilise de l'UDP sans aucune sécurité : pas de confidentialité, pas d'authenticité.
Il existe DNSSEC , qui permet de signer les réponses sans solution simple pour récupérer la clef en toute confiance et qui ne chiffre pas l'échange, et donc n'apporte aucune confidentialité.
DNSCrypt propose de la signature et du chiffrage. Il a décidé de snober l' IETF et n'a pas soumis une RFC, il n'a aucune chance de devenir un standard.
DOT , DNS Over TLS. Chiffrer de l'UDP existe ( DTLS ) mais c'est compliqué. C'est tellement plus simple en TCP avec TLS. Le protocole sait aussi fonctionner en TCP, il est donc trivial d'ajouter la couche de sécurité.
Le service DNS est globalement indiscret, le nom de domaine du site que vous allez visiter ainsi que le moment où vous le demandez sont des super- métadonnées sur votre activité en ligne. Le service DNS est un point central d'Internet, facile à censurer. On a pu voir dans l'actualité des images de murs tagués avec des ip de DNS non contrôlés par leurs gouvernements.
Image de Kaan Sezyum via Twitter.
DoH (DNS-over-HTTPS), le dernier de cette lignée, propose du chiffrage, mais surtout passe par HTTP, rendant plus compliqué la censure. Il suffit d'une URL sur un gros site bien légitime pour mettre à disposition un service DNS. Les butineurs contemporains savent déjà l'utiliser, et ça arrive dans les OS des ordiphones puis des ordinateurs.
Protocole et RFC
Pour implémenter un protocole ce n'est pas compliqué : il suffit d'aller lire sa RFC, puis d'écrire du code. Pour le DoH, c'est la RFC 8484 .
Le concept du DoH est simple : faire causer un serveur DNS à l'intérieur d'une requête HTTP. Tellement simple que l'échange utilise le même format binaire que le très tradionnel protocole UDP "over" DNS.
La question passe soit en GET, avec un payload encodé en base64, soit en POST, avec le payload dans le corps de la requête HTTP.
La documentation insiste beaucoup sur l'importance d'HTTP/2 plutôt que la classique version HTTP/1.1, et c'est avec cette version que les butineurs, première cible du DOH, vont causer aux serveurs DoH.
Implémentation
Python est le roi du prototypage avec une logithèque bien fournie. Bon, HTTP/2 a des implémentations matures cotés serveurs, mais c'est un peu plus en mode "peinture fraîche" côté client, hors écosystème Golang, premier lobbyiste HTTP/2 du Monde. Dans Python, il faut lâcher l'éternel Requests , pour Httpx comme bibliothèque HTTP.
Pour le DNS, la bibliothèque dnspython n'est pas super élégante, mais elle est très complète. Oui, dnspython fait du DoH de base, mais en HTTP/1.1, et ça coupe un peu la démonstration pédagogique.
#!/bin/env python3 import httpx from dns import message, rdatatype class Resolver: def __init__(self, server, client=None): self.server = server if client is None: client = httpx.Client(http2=True) self.client = client
Une class pour tout ranger, avec un client HTTP/2 que l'on peut réutiliser pour profiter du multiplexage des échanges. Techniquement, je peux envoyer une brouette de requête dans une même connexion TCP et recevoir les réponses dans leur ordre d'arrivée si j'utilise la version asynchrone du client.
def query(self, queryname, rdtype=rdatatype.ANY): q = message.make_query(queryname, rdtype) # dnspython génère la requête au format wire response = self.client.post(self.server, headers={ "Accept": "application/dns-message", "Content-Type": "application/dns-message" }, content=q.to_wire()) assert response.status_code == 200 return message.from_wire(response.content) # dnspython lit le format wire
Une requête POST
avec les en-têtes requis et le blob binaire dans le corps. Une vérification pédagogique du statut de la réponse, puis sa désérialisation.
if __name__ == '__main__': r = Resolver('https://cloudflare-dns.com/dns-query') print(r.query('bearstech.com', 'TXT'))
Je choisis le DOH de Cloudflare, parce que pourquoi pas, et je lui demande ce qu'il pense des TXT
de bearstech.com
.
DOH tient ses promesses : avec une lib DNS et une lib HTTP, on peut coder très rapidement du DoH.