# The mechanism and defense of DNS spoofing and cache poisoning [2026] — protect name resolution with RFC 5452 and DNSSEC

> A systematic explanation of DNS spoofing / cache poisoning that hijacks name resolution, from the principle of the Kaminsky attack to the defenses of RFC 5452 and DNSSEC (RFC 4033-4035). Why UDP DNS believes forged answers, how source-port randomization and the transaction ID create unpredictability, and DNSSEC signature verification and encryption via DoH/DoT — shown with type-safe code and configuration examples. All are legal procedures confined to your own resolver.

- Published: 2026-06-28
- Author: 友田 陽大
- Tags: セキュリティ, ネットワーク, TCP/IP, 脆弱性診断, ホワイトハッカー, 倫理的ハッキング
- URL: https://tomodahinata.com/en/blog/dns-spoofing-cache-poisoning-dnssec-defense-guide
- Category: 実践ネットワーク攻撃と防御
- Pillar guide: https://tomodahinata.com/en/blog/network-penetration-testing-methodology-attack-defense-guide

## Key points

- DNS spoofing hijacks name resolution and points a correct domain (bank.example) at the attacker's IP. Cache poisoning injects a forged answer into the resolver's cache, poisoning many users at once.
- The condition for success is 'returning the correct forged answer faster than the legitimate response.' The keys are the 16-bit transaction ID and the source port. The 2008 Kaminsky attack made this guessing practical and forced a redesign of DNS.
- RFC 5452's defense is 'make guessing hard' — combining source-port randomization (16 bits) and the transaction ID raises entropy to the 32-bit class, making brute force impractical.
- The essential defense is DNSSEC (RFC 4033-4035). Authoritative servers digitally sign responses, and the resolver verifies the signature. Forged answers don't match the signature and are rejected = ending 'first to say it wins' with cryptography.
- The path-side defense is DoH/DoT (DNS over HTTPS/TLS, RFC 8484/7858). It encrypts the route to the resolver and prevents sniffing/tampering. DNSSEC (authenticity) and DoH/DoT (confidentiality) are complementary; you need both.

---

The instant you type `bank.example` into your browser, the first thing that happens is **name resolution** — a DNS query that converts the domain name into an IP address. What if this one move is hijacked by an attacker? With the correct domain still shown in the URL bar, **only the connection destination is swapped to the attacker's server.** This is **DNS spoofing**, and **cache poisoning**, which poisons the resolver's cache itself, is an extremely wide-reaching attack that drags in many users with a single success.

Following [the big picture of network pentesting](/blog/network-penetration-testing-methodology-attack-defense-guide), this article unpacks the principle of DNS attacks from the historic Kaminsky attack and shows **defense via RFC 5452 and DNSSEC** with configuration and code. At the root is [TCP/IP's premise of trust](/blog/tcp-ip-protocol-suite-fundamentals-complete-guide) that early DNS believes an "unauthenticated response" over [UDP](/blog/tcp-vs-udp-quic-http3-difference-when-to-use-guide).

> **Thoroughness of the safe zone**: all verification in this article is done only against **a DNS resolver you stood up yourself** (e.g. a local BIND/Unbound experiment box) in an [isolated lab](/blog/ethical-hacking-home-lab-kali-juice-shop-ctf-self-study-roadmap-guide). **Injecting forged answers into a public resolver or someone else's DNS is a violation of the Unauthorized Access Act and an attack on social infrastructure.** What you may verify is only your own assets — always read the [legal article](/blog/ethical-hacker-law-japan-unauthorized-access-act-active-cyber-defense-disclosure-guide) first.

---

## 1. How DNS gets tricked — "a fast, plausible forged answer"

When a caching DNS resolver receives a query for `bank.example` not yet in its cache, it queries the upstream authoritative server. The attacker's aim is to **make the resolver believe a forged response before the legitimate authoritative server's response.**

For the resolver to accept the forged answer, the forged answer must **satisfy all** of the following.

```text
正規応答とマッチさせるべき要素：
  1. トランザクションID（16ビット）が問い合わせと一致
  2. 送信元/宛先ポートが一致
  3. 質問セクション（QNAME/QTYPE）が一致
  4. 正規応答より「速く」届く（レース）
```

Resolvers once **fixed the source port**, and there were implementations where the transaction ID was easy to predict. Then what the attacker must guess is effectively **just the 16-bit ID (65,536 possibilities)**. Furthermore, if it's cached, you have to wait until the next TTL, but —

### 1.1 The Kaminsky attack (2008) — the single blow that forced DNS to be redesigned

In 2008, Dan Kaminsky disclosed a decisive method. **Query a large number of non-existent subdomains (`random123.bank.example`)** and, for each, fire a **forged answer containing a delegation (NS/glue)** saying "the authoritative server for `bank.example` is the attacker's IP." Because the names don't exist, you can avoid waiting for the cache TTL and try at high speed any number of times. Once you hit, the authority for the entire domain is etched into the cache as poison.

This showed "guessing the 16-bit ID can realistically be broken" and **forced a fundamental rethink of DNS defense.** The answers are the following RFC 5452 and DNSSEC.

---

## 2. Defense ①: RFC 5452 — make guessing "impractical"

[RFC 5452](https://www.rfc-editor.org/rfc/rfc5452.html) (Measures for Making DNS More Resilient against Forged Answers, 2009) standardized implementation rules that **make forged answers hard to accept.** The center is **adding entropy.**

- **Source-port randomization**: choose the source UDP port randomly per query. The attacker must guess **the port (up to 16 bits)** in addition to the ID, so the combined entropy jumps to effectively **about 32 bits.** Brute force becomes impractical.
- **Transaction-ID randomization**: avoid predictable IDs and randomize them cryptographically.
- **0x20 encoding** (auxiliary): randomize the case of letters in QNAME and use whether it's preserved in the response as additional verification material.

```bash
# 自分のリゾルバが送信元ポートをランダム化しているかを確認（自分の資産で）
# 良い例：問い合わせごとに source port がばらける（高エントロピー）
dig +short porttest.dns-oarc.net TXT @127.0.0.1
#   → "GREAT" 等（リゾルバのポートランダム性の評価が返る）
```

> **An important limitation**: RFC 5452 is a probabilistic defense that "makes guessing hard," not one that "makes it impossible." Entropy is meaningless against an attacker on the path (already MITM). So the next, DNSSEC, is needed.

---

## 3. Defense ②: DNSSEC — end "first to say it wins" with cryptography

[DNSSEC](https://www.rfc-editor.org/rfc/rfc4033) (RFC 4033/4034/4035) is the **essential answer** to DNS attacks. The idea is clear — **authoritative servers digitally sign responses, and the resolver verifies that signature.** Forged answers can't produce a correct signature, so they're rejected at verification.

```text
DNSSEC の信頼の連鎖（chain of trust）：
  ルート(.) の鍵 ──署名──► .example の鍵 ──署名──► bank.example のレコード
       ▲                                                    │
       └──── リゾルバはルートの鍵(トラストアンカー)から ◄────┘
              署名を辿って検証。1つでも署名が合わなければ SERVFAIL（拒否）
```

- **RRSIG**: the signature over each record set.
- **DNSKEY**: the public key used for verification.
- **DS**: placed in the parent zone, guaranteeing the child zone's key (the link of the chain).

When verification succeeds, the **AD (Authenticated Data) bit** is set in the response. Forged answers fail signature verification, and the resolver returns no result.

```bash
# DNSSEC 検証が効いているかを確認（AD フラグが立つか）
dig +dnssec bank.example @127.0.0.1
#   ヘッダの flags に "ad" があれば、署名検証済みの真正な応答
#   RRSIG レコードが応答に含まれる
```

### 3.1 "Require" verification on the app/resolver side

Even if the authoritative zone is signed, it's meaningless unless the resolver verifies. **Using a verifying resolver** is the user side's responsibility.

```ts
import { Resolver } from "node:dns/promises";

/**
 * DNSSEC 検証を行うリゾルバ（例：ローカルの validating resolver）を明示的に指定する。
 * OS のデフォルトに任せず「検証するリゾルバ」へ向けることで、毒入りキャッシュを避ける。
 * 失敗（SERVFAIL）は握りつぶさず例外として扱い、安全側（接続しない）に倒す。
 */
export async function resolveValidated(hostname: string): Promise<readonly string[]> {
  const resolver = new Resolver();
  resolver.setServers(["127.0.0.1"]); // 検証する自前/信頼リゾルバ（Unbound 等）
  try {
    return await resolver.resolve4(hostname);
  } catch (err) {
    // SERVFAIL は「署名検証に失敗した可能性」を含む。曖昧なまま接続しない。
    throw new Error(`DNS resolution failed (possible DNSSEC failure) for ${hostname}: ${(err as Error).message}`);
  }
}
```

---

## 4. Defense ③: DoH / DoT — encrypt the path

DNSSEC protects the **authenticity (whether it's been tampered with)** of the response, but not its **confidentiality (so others can't peek at who looked up what).** Also, the route **from the user to the resolver** is still plaintext. What fills this is path encryption.

- **DoT (DNS over TLS, [RFC 7858](https://www.rfc-editor.org/rfc/rfc7858))**: carry DNS over TLS (port 853).
- **DoH (DNS over HTTPS, [RFC 8484](https://www.rfc-editor.org/rfc/rfc8484))**: carry DNS over HTTPS (443). Strong against censorship and sniffing.

```text
役割分担（両方が要る）：
  DNSSEC  … 応答の真正性（偽答を弾く）        ← 権威〜リゾルバの「中身」を守る
  DoH/DoT … 経路の機密性・完全性（盗聴/改ざん） ← 利用者〜リゾルバの「経路」を守る
```

**DNSSEC and DoH/DoT are complementary, not competing.** The former is "is the answer genuine," the latter is "is the path being peeked at." In production, combine both.

---

## 5. A checklist for designers

Points to confirm in design and operations to protect your domain and name resolution.

- [ ] Is **your authoritative zone DNSSEC-signed** (register the DS with the parent via your domain registrar/DNS service).
- [ ] Does the **resolver your app points at perform DNSSEC verification** (for a managed resolver, confirm the verification policy).
- [ ] Does the resolver **randomize the source port** (RFC 5452-compliant).
- [ ] Can you use **DoH/DoT** between client and resolver.
- [ ] Do you **not swallow DNS resolution failures (SERVFAIL)** and fall to the safe side (abort connection).
- [ ] After all, **the final wall is TLS certificate verification** — even if name resolution is poisoned, TLS won't connect to an attacker's server lacking a legitimate certificate (see the [MITM article](/blog/arp-spoofing-mitm-attack-detection-defense-guide)).

> **Think in layers**: even if DNS is poisoned, TLS protects; the MITM targeting TLS is protected by mTLS and zero trust. **Not depending on a single defense** is the consistent stance of network security.

---

## 6. Summary

- **DNS spoofing/cache poisoning** hijacks name resolution. The Kaminsky attack made the danger of 16-bit ID guessing real and forced a redesign of DNS defense.
- **RFC 5452**: source-port + ID randomization raises entropy to the ~32-bit class. A probabilistic defense that makes guessing impractical (powerless against an on-path attacker).
- **DNSSEC (RFC 4033-4035)**: an essential defense that rejects forged answers via signatures and a chain of trust. The AD bit is proof of authenticity.
- **DoH/DoT (RFC 7858/8484)**: path encryption. Complementary to DNSSEC (authenticity); you need both.
- **In the end, TLS again**: even if name resolution is poisoned, certificate verification refuses the attacker's server. Defense in depth.

Next, we'll handle **[TCP session hijacking / RST injection / IP spoofing](/blog/tcp-session-hijacking-rst-injection-ip-spoofing-defense-guide)**, which seizes the established TCP connection itself, including defense via RFC 5961.

---

I (Hinata Tomoda) have implemented production DNS design (DNSSEC, private DNS, fail-safe on resolution failure) and "don't fully trust name resolution" designs including TLS/certificate operations. "I want to DNSSEC-sign our domain," "DNS resolution failures are causing outages," "I want to introduce DoH/DoT" — I diagnose such hardening of name resolution from the attacker's perspective and implement it with the two wheels of authenticity and confidentiality. Feel free to reach out.
