DNS is the phonebook of the internet, and it was designed in 1987 with zero security. Every DNS query you make is unencrypted, unauthenticated, and trivially intercepted.
How DNS Poisoning Works
Traditional DNS uses UDP on port 53 with no encryption and no authentication. An attacker can intercept or forge DNS responses to redirect users to malicious servers.
Cache Poisoning
An attacker sends forged DNS responses to a resolver, polluting its cache. Every user of that resolver then gets the wrong IP address:
User → "What is the IP for bank.com?"
Resolver → queries authoritative DNS
Attacker → sends forged response: "bank.com = 185.192.x.x" (attacker's server)
Resolver → caches the fake record
All users → connect to attacker's server for the next hour
The attacker needs to guess the query ID (16-bit, only 65,536 possibilities) and respond before the legitimate server. The Kaminsky attack demonstrated this is practical.
On-Path Attacks
On a compromised network (public Wi-Fi, ISP-level), an attacker can simply intercept DNS queries and respond with malicious answers:
# On the attacker's machine (ARP spoofing + DNS interception)
# All DNS queries from the victim resolve to the attacker's IP
iptables -t nat -A PREROUTING -p udp --dport 53 -j REDIRECT --to-port 53The user sees the correct domain in their browser, but connects to the attacker's server. With a valid TLS certificate (via Let's Encrypt for a lookalike domain) or by targeting HTTP endpoints, this is invisible.
DNSSEC: Authenticating DNS Responses
DNSSEC (DNS Security Extensions) adds cryptographic signatures to DNS records. The resolver can verify that the response actually came from the authoritative server and was not tampered with.
How the Chain of Trust Works
Root Zone (.) → signs .com zone key
.com zone → signs yoursite.com zone key
yoursite.com → signs individual DNS records (A, MX, etc.)
Each level signs the keys of the level below it, creating a chain of trust from the root to individual records.
DNSSEC Record Types
| Record | Purpose |
|---|---|
| RRSIG | Signature over a DNS record set |
| DNSKEY | Public key used to verify RRSIG |
| DS | Hash of a child zone's DNSKEY (delegation) |
| NSEC/NSEC3 | Authenticated denial of existence |
Checking DNSSEC
# Check if a domain has DNSSEC
dig +dnssec yoursite.com A
# Look for the 'ad' flag (Authenticated Data)
dig +dnssec +short cloudflare.com A
# If your resolver supports DNSSEC validation, the 'ad' flag appears
# Verify the DNSSEC chain
delv @8.8.8.8 cloudflare.com A +rtraceEnabling DNSSEC for Your Domain
Most DNS providers support DNSSEC with a one-click enable:
- Enable DNSSEC in your DNS provider (Cloudflare, Route53, etc.)
- Add the DS record to your domain registrar
- Verify with
dig +dnssec
# Verify DNSSEC is working
dig +dnssec +short example.com
# Check for RRSIG records
dig RRSIG example.comDNSSEC Limitations
DNSSEC authenticates DNS responses but does not encrypt them. An observer can still see which domains you are querying. For privacy, you need DoH or DoT.
DNS over HTTPS (DoH)
DoH encrypts DNS queries inside HTTPS, making them indistinguishable from regular web traffic on port 443.
# Query using DoH with curl
curl -H "accept: application/dns-json" \
"https://cloudflare-dns.com/dns-query?name=example.com&type=A"// Programmatic DoH query in JavaScript
async function dohQuery(domain) {
const response = await fetch(
`https://cloudflare-dns.com/dns-query?name=${domain}&type=A`,
{ headers: { accept: "application/dns-json" } }
);
const data = await response.json();
return data.Answer.map((a) => a.data);
}DoH Providers
| Provider | Endpoint |
|---|---|
| Cloudflare | https://cloudflare-dns.com/dns-query |
https://dns.google/dns-query | |
| Quad9 | https://dns.quad9.net/dns-query |
Configuring DoH System-Wide
# Firefox: Settings → Privacy & Security → Enable DNS over HTTPS
# Chrome: Settings → Security → Use secure DNS
# Linux (systemd-resolved)
# /etc/systemd/resolved.conf
[Resolve]
DNS=1.1.1.1#cloudflare-dns.com
DNSOverTLS=yesDNS over TLS (DoT)
DoT encrypts DNS queries using TLS on port 853. Unlike DoH, it is easy to identify and block at the network level (since it uses a dedicated port), but it is simpler to implement.
# Test DoT with kdig
kdig -d @1.1.1.1 +tls-ca example.com A
# Or with openssl
echo -ne '\x00\x1d\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x07example\x03com\x00\x00\x01\x00\x01' | \
openssl s_client -connect 1.1.1.1:853 -quietDoH vs DoT
| Feature | DoH | DoT |
|---|---|---|
| Port | 443 (blends with HTTPS) | 853 (dedicated) |
| Blockable | Hard (looks like HTTPS) | Easy (block port 853) |
| Privacy from ISP | Strong | Moderate |
| Enterprise control | Harder to monitor | Easier to monitor |
| Standard | RFC 8484 | RFC 7858 |
For personal use: DoH provides better privacy because it cannot be selectively blocked.
For enterprise networks: DoT is easier to manage because security teams can control and monitor it.
Monitoring and Defense
For application developers
// Use DoH in your application for DNS resolution
// This prevents DNS-level attacks on your users
import { Resolver } from "dns/promises";
const resolver = new Resolver();
resolver.setServers(["https://cloudflare-dns.com/dns-query"]);For infrastructure teams
- Enable DNSSEC validation on your resolvers
- Monitor for DNS anomalies (high NXDomain rates, unusual query patterns)
- Use RPZ (Response Policy Zones) to block known malicious domains
- Log DNS queries for forensic analysis
# Check your resolver's DNSSEC validation
dig +dnssec @your-resolver example.com
# Look for 'ad' flag in the responseConclusion
DNS was built without security, and decades later we are still retrofitting it. DNSSEC authenticates responses to prevent poisoning. DoH and DoT encrypt queries to prevent eavesdropping. Use all three: enable DNSSEC on your domains, configure DoH or DoT on your resolvers, and monitor your DNS traffic for anomalies. The effort is minimal — the protection is significant.