Hoe DNSSEC een DNS-antwoord ondertekent
Als je resolver vraagt welk IP-adres bij wallieweb.nl hoort, krijgt hij een antwoord terug. Gewone DNS biedt geen manier om te controleren of dat antwoord echt van de juiste nameserver komt en onderweg niet is aangepast. DNSSEC voegt die controle toe: elk antwoord krijgt een digitale handtekening, en de sleutel waarmee je die controleert, is zelf weer ondertekend door de laag erboven. Zo loopt er een keten van wallieweb.nl via .nl naar de root.
In het artikel over DNS kwam het RRSIG-record al even voorbij. Hier volg ik die keten helemaal, met dig vanaf een Nederlandse verbinding op 19 oktober 2026. Onderweg reken ik de handtekening en de hash zelf na in Python. En de root bleek acht dagen voordat ik keek van sleutel te zijn gewisseld.
De handtekening bij het antwoord
Een resolver krijgt alleen DNSSEC-records als hij erom vraagt, met de DO-vlag (DNSSEC OK) in zijn vraag. Zonder die vlag behandelt een nameserver handtekeningen volgens RFC 4035 als gewone records en voegt hij ze niet uit zichzelf toe. Met dig +dnssec zet je de vlag aan:
wallieweb.nl. 120 IN A 52.236.137.214
wallieweb.nl. 120 IN RRSIG A 13 2 120 20261029000000 20261008000000 54655 wallieweb.nl. M0+MXg5K...
Het antwoord van de nameserver groeit daardoor van 57 naar 165 bytes. De velden van het RRSIG-record zijn beschreven in RFC 4034. 13 is het algoritme: ECDSA op curve P-256 met SHA-256. 2 is het aantal labels in de naam (wallieweb en nl), 120 de oorspronkelijke TTL. Dan volgen twee tijdstippen: de handtekening is geldig van 8 oktober tot 29 oktober 2026. Een validerende resolver moet controleren dat de huidige tijd daartussen ligt. 54655 is de key tag, een kort nummer voor de gebruikte sleutel, en wallieweb.nl. de zone die tekent.
De sleutel erbij halen
De publieke sleutel staat in de zone zelf, als DNSKEY-record:
wallieweb.nl. 3600 IN DNSKEY 257 3 13 Imk9c+VrWXyfmOrFmdSjr6UJTku/...
Het protocolveld is altijd 3, de 13 is het algoritme. De key tag staat niet in het record; je rekent hem uit met een checksum over de sleutelgegevens (bijlage B van RFC 4034). Dat levert 54655 op, dus dit is de sleutel die hoort bij de handtekening van het A-record.
Controleren gaat zo. Volgens RFC 4034 dekt de handtekening de velden van het RRSIG-record zelf (zonder de handtekening) plus de records die worden ondertekend, in een vaste canonieke vorm: namen in kleine letters, TTL gelijk aan de oorspronkelijke TTL. Voor het A-record van wallieweb.nl zijn dat precies 60 bytes. Ik liet de cryptography-bibliotheek in Python de handtekening van 64 bytes daarover controleren met de sleutel van 64 bytes: geldig. Daarna veranderde ik één bit, zodat het IP-adres 52.236.137.215 werd. Toen faalde de controle direct.
Maar wie een antwoord kan vervalsen, kan ook een eigen sleutel meesturen. Waarom zou je deze sleutel vertrouwen?
Een verwijzing in de laag erboven
Daarvoor staat er in de .nl-zone, bij SIDN, een DS-record (delegation signer) voor wallieweb.nl:
wallieweb.nl. 3600 IN DS 54655 13 2 E2BA90DD346D1AD9034D62DB322A4CAD7AAA9B3D94CDDA9B491DDB8E032FAA84
Het DS-record bevat geen sleutel, alleen een hash ervan. Volgens RFC 4034 is dat de hash over de naam van de zone plus de gegevens van het DNSKEY-record. Digest-type 2 is SHA-256. Ik rekende het na: SHA-256 over de bytes van wallieweb.nl gevolgd door de sleutelgegevens geeft exact E2BA90DD…FAA84.
Dat DS-record is op zijn beurt ondertekend door .nl, met een sleutel met key tag 60847. Daarmee verschuift de vraag één laag omhoog.
Twee sleutels of één
Bij .nl zie je twee sleutels:
nl. 3600 IN DNSKEY 256 3 13 ... ; key id = 60847
nl. 3600 IN DNSKEY 257 3 13 ... ; key id = 17153
De vlag 256 betekent "zonesleutel". Bij 257 staat daarnaast het bit voor secure entry point aan. SIDN tekent de gewone records in de zone met sleutel 60847, zoals het DS-record van wallieweb.nl. Sleutel 17153 tekent de verzameling DNSKEY-records zelf, en naar die sleutel verwijst het DS-record in de root. Zo kan de werksleutel worden vervangen zonder dat er in de root iets verandert.
Wallieweb.nl doet het eenvoudiger. Daar is er maar één sleutel (54655) die alles tekent, ook zijn eigen DNSKEY-record. Beide opzetten zijn geldig: volgens RFC 4034 mag een validator zijn controle niet laten afhangen van het entry point-bit.
De root, met een verse sleutel
In de root staat het DS-record voor .nl (verwijzend naar 17153), ondertekend door rootsleutel 8763. Hier is het algoritme 8: RSA met SHA-256, met sleutels van 2048 bits. Dat zie je terug in de grootte: een handtekening van de root is 256 bytes, een ECDSA-handtekening van .nl of wallieweb.nl 64 bytes.
Boven de root is er niets meer. De sleutel bovenaan, de root KSK, zit daarom ingebouwd in validerende software, net als rootcertificaten in je browser, wat ik eerder beschreef in hoe je browser een certificaat vertrouwt. Toen ik keek wie de rootsleutelset tekende, was dat key tag 38696, met een handtekening die op 11 oktober 2026 ingaat.
Volgens IANA is dat KSK-2024. Die sleutel staat sinds 11 januari 2025 in de rootzone en tekent sinds 11 oktober 2026; de vorige sleutel, KSK-2017 met key tag 20326, tekent sindsdien niet meer. Hij staat nog in de zone tot hij in maart 2027 verdwijnt. In het bind.keys-bestand op mijn Debian-server stonden beide sleutels al klaar. Een resolver die alleen de oude sleutel kent en die niet automatisch bijwerkt, kan de keten sinds 11 oktober niet meer op zijn trust anchor laten aansluiten.
Wat de resolver ermee doet
RFC 4035 onderscheidt onder meer secure (de keten klopt tot de trust anchor), insecure (aantoonbaar niet ondertekend) en bogus (er is een keten, maar die klopt niet). Bij een validerende publieke resolver zie je het verschil:
| Domein | DS in de ouderzone | Uitkomst |
|---|---|---|
wallieweb.nl | ja | antwoord met vlag ad |
google.com | nee | antwoord zonder ad |
dnssec-failed.org | ja, maar klopt niet | SERVFAIL |
De ad-vlag (authenticated data) mag een server volgens RFC 4035 alleen zetten als hij alle records in het antwoord authentiek vindt. Google.com heeft geen DS-record in .com. De resolver weet daardoor dat de zone niet is ondertekend en geeft het antwoord zonder ad.
Bij dnssec-failed.org kwam bij drie publieke resolvers SERVFAIL terug, de foutcode die RFC 4035 voorschrijft voor bogus data. Twee gaven er een Extended DNS Error (RFC 8914) bij met code 9, DNSKEY Missing. Geen enkele sleutel past bij het DS-record in .org. De derde meldde alleen code 6, DNSSEC Bogus. Met +cd (controle uit) krijg je het adres alsnog.
Bewijzen dat iets niet bestaat
Je kunt niet vooraf voor elke denkbare naam "bestaat niet" ondertekenen. DNSSEC tekent daarom de gaten tussen namen die wel bestaan. Wallieweb.nl doet dat met NSEC3 (RFC 5155), dat gehashte namen gebruikt zodat je niet van gat naar gat alle namen in de zone kunt opsommen.
Op bestaatniet.wallieweb.nl kwam NXDOMAIN terug met drie ondertekende NSEC3-records. Ik rekende de SHA-1-hashes na. wallieweb.nl hasht naar b47su27i…, de naam van het eerste record: de zone bestaat. bestaatniet.wallieweb.nl hasht naar 274q6d6p…, wat in het gat tussen 21ec0cks… en 2spupdin… valt. En het derde gat dekt qm2h30up…, de hash van *.wallieweb.nl, dus er is ook geen wildcard. De zone hasht zonder salt en zonder extra rondes, zoals RFC 9276 sinds 2022 aanraadt.
Zelf proberen
Met delv, dat bij BIND hoort, zie je hoe een validator de keten ophaalt:
delv @8.8.8.8 wallieweb.nl A +rtrace
;; fetch: wallieweb.nl/A
;; fetch: wallieweb.nl/DNSKEY
;; fetch: wallieweb.nl/DS
;; fetch: nl/DNSKEY
;; fetch: nl/DS
;; fetch: ./DNSKEY
; fully validated
wallieweb.nl. 120 IN A 52.236.137.214
Een kapotte keten, met en zonder controle:
dig dnssec-failed.org A @1.1.1.1
dig +cd dnssec-failed.org A @1.1.1.1
;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL
; EDE: 9 (DNSKEY Missing): (no SEP matching the DS found for dnssec-failed.org.)
;; ->>HEADER<<- opcode: QUERY, status: NOERROR
dnssec-failed.org. 133 IN A 96.99.227.255
En de schakels los: dig +dnssec +multi nl DNSKEY en dig +dnssec nl DS @k.root-servers.net. Met +multi zet dig de key tag als commentaar achter elke sleutel.
Tot slot
Wat me verraste, is hoe klein de cryptografie is. Het bewijs voor 52.236.137.214 zelf is een handtekening van 64 bytes over 60 bytes data, en de rest van de keten bestaat uit een handvol hashes en handtekeningen. Het lastige zit in de tijd: de handtekeningen van wallieweb.nl verlopen na drie weken en moeten steeds opnieuw worden gezet, en de root wisselde vlak voordat ik ging kijken van sleutel, na acht jaar met dezelfde. Dat ik dat aan één key tag in de dig-uitvoer zag, was het leukste moment van dit uitzoekwerk.