为什么DMARC的新“NP”标签在与DNSSEC一起使用时会失败
摘要
文章详细介绍了DMARC的“np”标签(RFC 9989)与DNSSEC的紧凑型否认存在(RFC 9824)之间新发现的不兼容问题,导致该标签在使用DNSSEC且配合主流DNS提供商时出现故障。IETF已确认此问题,但尚未找到解决方案。
暂无内容
查看缓存全文
缓存时间: 2026/07/05 16:32
# 为什么 DMARC 的新版 "np" 标签可能在 DNSSEC 下失效
来源:https://dmarcwise.io/blog/dmarc-np-incompatibility-with-dnssec
最近更新的 DMARC 规范(以 RFC 9989 发布)引入了新的 `np` 标签。其用途是指定当发送方域名是 **DMARC 记录所在域名的某个不存在的子域名** 时,接收方应应用的策略。
我们发现 RFC 9989 中对“不存在域名”的定义与另一个最近的规范 RFC 9824(即“**DNSSEC 中紧凑的不存在证明**”)存在冲突,导致 `np` 标签并不总能按预期工作。虽然 DNSSEC 的使用远未普及,但该问题会影响所有使用 DNSSEC 且依赖于主要 DNS 提供商(如 **Cloudflare、NS1、AWS Route 53 和 Azure**)的域名。
我们已经向负责 DMARC 的 IETF 工作组提出了该问题:该问题已被承认,但尚未达成任何解决方案。在本文中,我们将讲述整个故事,并尝试评估这种不兼容性的影响。
本文由人类撰写,非 AI 生成。
## 新的 `np` 标签
2026 年 5 月,IETF 发布了 DMARC 规范的更新版本,包含三份文档。RFC 9989 引入了新的 DMARC 记录标签 `np`,代表 **不存在的子域名策略**。
在 DMARC 记录中,它看起来像这样:
``
v=DMARC1; p=none; sp=quarantine; np=reject;
``
`p` 标签指定的策略适用于发布该记录的主域名,`sp` 标签适用于没有发布自身 DMARC 记录的现有子域名,而 `np` 标签则适用于不存在的子域名。
当你希望在未使用的子域名上“阻止”恶意邮件,同时在其他子域名上保持较宽松的策略时,设置不同的策略会非常有用。
## DNS 中的不存在的子域名
DMARC RFC 对不存在域名的定义如下:
> 就 DMARC 而言,不存在域名的含义与 RFC 8020 中的描述一致。也就是说,如果对某个域名的查询返回的响应码为 NXDOMAIN,则该域名及其所有可能的子域名都不存在。
这是一个常见的定义,没什么令人惊讶的。DNS 服务器通常返回 `NXDOMAIN` 响应码,以表示被查询的域名及其所有子域名不存在,即它们没有任何关联的 DNS 记录。以下是一个例子:
``
~ ❯ dig non-existent-subdomain.rai.it +noall +comments +answer
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 17031
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
``
另一种情况是,所请求的特定资源记录类型未找到,但被查询域名的其他记录类型存在。在这种情况下,DNS 名称存在,因此服务器不能返回 `NXDOMAIN`。它会使用带有空 ANSWER 部分的 `NOERROR`,也称为 `NODATA`。
(DMARC 的一个先前实验性扩展 RFC 9091 规定,不存在域名是指对 A、AAAA 和 MX 记录的查询返回 `NXDOMAIN` 或 `NODATA` 响应的域名,并明确指出这比 RFC 8020 中的定义更广泛。该定义在纳入 RFC 9989 时被更改。)
在上面的例子中,我们可以确定 `non\-existent\-subdomain\.rai\.it` 名称及其所有子域名都不存在。
另一方面,在下面的例子中,我们可以确定域名 `news\.rai\.it` 没有 `MX` 记录,但它存在,因为它有其他记录类型:
``
~ ❯ dig news.rai.it MX +noall +comments +answer
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 37891
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
``
在 DNSSEC 的世界里,情况会变得更加复杂。
## DNSSEC 简介
DNSSEC 是 DNS 的一种安全扩展,让解析器能够验证 DNS 答案的真实性,并确保其未被篡改。它通过添加加密签名(解析器可以验证)来实现这一点。
在下面的例子中,我们显式要求 DNS 解析器返回 DNSSEC 相关数据。我们可以看到 DNS 响应在 ANSWER 部分包含一条额外的记录,类型为 `RRSIG`。
``
~ ❯ dig dmarcwise.io +dnssec +noall +comments +answer
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 49227
;; flags: qr rd ra; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags: do; udp: 1232
;; ANSWER SECTION:
dmarcwise.io. 892 IN A 94.237.30.173
dmarcwise.io. 892 IN A 94.237.26.215
dmarcwise.io. 892 IN RRSIG A 13 2 900 20260801080000 20260704073000 20768 dmarcwise.io. bPF4BFfnETKC4GGwrmi0xlOzvBaotX8DG5E8/rpmelxoXJ2eA8RJtUrN JVFjFVCq5ZP/Ul9XYgT86c8Vw5Y4Sw==
``
`RRSIG` 记录包含解析器要验证的签名。验证过程涉及查询区域中的其他记录(`DNSKEY`)以及父区域中的记录(`DS`),并沿着 DNS 层次结构向上直到根区域。
## DNSSEC 中的不存在证明
在标准 DNS 中,对不存在域名的查询会产生带有空 ANSWER 部分的回复,正如我们上面所见。这不利于 DNSSEC,因为空的 ANSWER 部分不包含任何记录,因此 **没有什么可以签名**。
DNSSEC 使用 `NSEC` 记录类型来解决此问题。它不是对不存在的名称进行签名,而是识别出在请求的名称之前的一个存在的名称,并报告规范 DNS 顺序中的下一个存在的名称,**从而证明请求的名称不存在于它们之间**。`NSEC` 记录会被签名,以便任何篡改都能被检测到。DNS 响应码仍然是 `NXDOMAIN`。
例如,一个区域可能包含以下域名:
``
a.example.com
z.example.com
``
当我们查询不存在的域名 `f\.example\.com` 时,响应会说:
``
a.example.com 300 IN NSEC z.example.com. A RRSIG NSEC
``
它通过告诉我们 `a\.example\.com` 存在,并且规范 DNS 顺序中的下一个域名是 `z\.example\.com`(具有记录类型 `A`、`RRSIG` 和 `NSEC`),来确认 `f\.example\.com` 不存在。两者之间没有其他东西存在。
实际上,这比这要稍微复杂一些,因为我们还需要第二个 `NSEC` 记录来证明没有通配符记录会覆盖 `f\.example\.com`。
一个例外是,当我们想说域名存在,只是没有那个记录类型时。在这种情况下,我们可以像上面一样只使用 **一个 `NSEC` 记录**,并列出存在的记录类型。
如今,许多 DNS 服务器使用 `NSEC` 的一种变体,称为 `NSEC3`,它使用 **散列名称** 而不是原始名称,以使攻击者更难以在 DNS 区域中“行走”并枚举其中的所有域名。为此,我们还需要第三个 `NSEC3` 记录来证明“最近 enclosure”存在。
在最坏的情况下,我们现在总共有 8 条记录(`SOA + SOA RRSIG + 3x NSEC3 + 3x NSEC3 RRSIG`),仅仅为了证明一个域名不存在。在某些情况下,这可能会接近 DNS UDP 数据包的大小限制,可能导致响应被截断并切换到 TCP。
``
~ ❯ dig non-existent.rcodezero.at +dnssec +noall +comments +authority
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 28077
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 8, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags: do; udp: 1232
;; AUTHORITY SECTION:
rcodezero.at. 30 IN SOA ns1.rcodezero.at. rcodezero.ipcom.at. 2025113549 10800 3600 604800 30
rcodezero.at. 30 IN RRSIG SOA 13 2 3600 20260716000000 20260625000000 61192 rcodezero.at. m2lRXWLkudZ/DMpS0VPiaNwkN2Wak44JjLNM2VsfY4pwOuVeEOE+Yip5 g4QaVC4vOhuTDikl6RG2XbTJhJIIjQ==
5fck5kit9i35fg68phiu98n07jqtcbe5.rcodezero.at. 30 IN NSEC3 1 0 0 - 5O2N7HE1TQ836ADQ0G40SEJUHNT866RF A NS SOA TXT AAAA RRSIG DNSKEY NSEC3PARAM
5fck5kit9i35fg68phiu98n07jqtcbe5.rcodezero.at. 30 IN RRSIG NSEC3 13 3 30 20260716000000 20260625000000 61192 rcodezero.at. Ix74l+UIyGSWfhymQJgrHRceKtzla5yCyF56yZfawS4JLak+ECXniLoM MMEZKF+2Q4jQ+UeLV6+oT54wov1YJw==
dqj11be2u7pvv034803fs4s4v1i77o5c.rcodezero.at. 30 IN NSEC3 1 0 0 - EH6FBIEDOMTC5LBAADQPMESG8V80K14I
dqj11be2u7pvv034803fs4s4v1i77o5c.rcodezero.at. 30 IN RRSIG NSEC3 13 3 30 20260716000000 20260625000000 61192 rcodezero.at. q7ydqY3L8MgC+HvuFVLSnuGkvoGLbWu8UDGFQgAPYU9wRQTTG2dmULl8 vFvPnXRWwIGzpMBQDzyODDCXnIjjPw==
goujsva9gi69p68758u2bmh1b09fm0d4.rcodezero.at. 30 IN NSEC3 1 0 0 - K93HCBG8K15UQVA0LORHV7CSPS32A51P A AAAA RRSIG
goujsva9gi69p68758u2bmh1b09fm0d4.rcodezero.at. 30 IN RRSIG NSEC3 13 3 30 20260716000000 20260625000000 61192 rcodezero.at. /dvDo61kcqHe6UN4YzMCXjGbSgsaC9CRNMmG30BaZPBDLJNrGxVMyMZs ijtpeHOrSfIfW/MPAi/s6Mp8sZ0zhw==
``
这种不存在证明方法的另一个问题是,一些 DNS 提供商(著名的有 Cloudflare)会即时签署 DNS 答案,而不是使用预先计算好的签名。这被称为 **在线签名**。问题是,要生成 `NSEC` 记录,服务器不仅需要知道记录本身的数据,还需要知道区域的其余部分,以确定前一条和下一条记录。
## 最小覆盖 NSEC 记录
首次尝试更好地支持在线 DNSSEC 签名是在 2006 年初,通过 RFC 4470(标题为“**最小覆盖 NSEC 记录和 DNSSEC 在线签名**”,非正式地称为“白色谎言”)引入的。
核心思想是,权威服务器不是用实际的域名来响应 `NSEC` 记录,而是可以构造一个稍微在被查询名称之前和之后的上一个和下一个名称。虽然这有助于在线签名部署 DNSSEC,但它仍然可能需要两条 `NSEC` 记录,因此 DNS 答案仍然比标准的 `NXDOMAIN` 答案要大得多。
## 紧凑的不存在证明
转折点出现在 2016 年,当时 Cloudflare 实现了 DNSSEC,并选择不使用标准的 `NSEC`、`NSEC3` 或白色谎言,而是使用了一种他们最初称为“黑色谎言”的新方法。
基本思想是对不存在的域名返回 `NOERROR` 响应,而不是 `NXDOMAIN`,声称该名称存在,但没有所查询类型的记录。使用这种方法,只需要一条 `NSEC` 记录。
DNS 答案因此变得非常紧凑:如下例所示,为了证明 `non\-existent\.ietf\.org` 不存在,只有一条 `NSEC` 记录声称下一个域名是 `\\000\.non\-existent\.ietf\.org`。
``
~ ❯ dig non-existent.ietf.org +dnssec +noall +comments +authority
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 1086
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 4, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags: do; udp: 1232
;; AUTHORITY SECTION:
ietf.org. 1726 IN SOA jill.ns.cloudflare.com. dns.cloudflare.com. 2408613648 10000 2400 604800 1800
non-existent.ietf.org. 1726 IN NSEC \000.non-existent.ietf.org. RRSIG NSEC TYPE128
non-existent.ietf.org. 1726 IN RRSIG NSEC 13 3 1800 20260705100533 20260703080533 34505 ietf.org. 4J0vQB8RCW6ZcVBz7euKBbQcGYIP3IS1QqACdSJHh9kCUGIaWxMgQyXq ujBP+LQvuoJcCxn8rSKYin3ZDX5wmw==
ietf.org. 1726 IN RRSIG SOA 13 2 1800 20260705100533 20260703080533 34505 ietf.org. 11YLhkreISTOxD0rO3OLTJUFtJOReQOxy9x6KzTXSTEENmoFuzriHr10 Q+j+pkrMde4qzp9X7J+OKlA0kBB1tQ==
``
从 DNSSEC 验证的角度来看,这种方法是有效的,因为它建立在现有规范之上,正如 Cloudflare 所说:
> RFC4470(白色谎言)允许我们在 `NSEC` 中随机生成下一个名称。也不设置第二个通配符子域名的 `NSEC` 是允许的,只要实际被查询的名称上存在一条 `NSEC` 记录。最后,我们为 `NODATA` 的 `NSEC` 记录设置所有记录类型的谎言也是可以的——毕竟域名在不断地变化,很有可能在 `NSEC` 记录告诉你 `blog\.cloudflare\.com` 上没有 `MX` 记录和你成功查询 `blog\.cloudflare\.com` 的 `MX` 记录之间的这段时间内,区域文件发生了变化。
现在,许多大型权威 DNS 提供商(包括 **Cloudflare、NS1、AWS Route 53 和 Azure DNS**)都使用这种方法(完整列表见下文)。
尽管这种方法很流行,但其主要缺点是你失去了显式的 `NXDOMAIN` 响应码。许多应用程序依赖它来确定域名何时不存在,包括在评估 `np` 标签时的 DMARC 软件(下文会详细说明)。
## 紧凑的不存在证明成为 RFC 9824,并引入 NXNAME 信号
在 Cloudflare 推出 DNSSEC“黑色谎言”之后,人们开始努力将其标准化,并为 `NXDOMAIN` 可见性问题找到合适的解决方案。这个过程于 2025 年 9 月完成,发布了 RFC 9824,即“**DNSSEC 中紧凑的不存在证明**”。
基础技术与 Cloudflare 自 2016 年以来使用的技术相同,但它进行了改进,以提高与仍然依赖读取 `NXDOMAIN` 响应码的 DNS 生态系统的兼容性。正如 RFC 的一位作者所解释的那样,这导致了 Salesforce 内部系统出现问题:
> 这种以 NODATA 而不是传统的 NXDOMAIN 响应来回应不存在名称查询的技术最初由 Cloudflare 设计。当 NS1(我们的一个关键 DNS 提供商)也实现该技术时,我开始介入,并且由于关键性 NXDOMAIN 信号的丢失,对 Salesforce 的各种工具产生了负面影响。当时我与 NS1 的 Jan Vcelak 合作设计了 ENT 哨兵,以便能够精确区分不存在的名称。2023 年初,Christian Elmerot(Cloudflare)和我一起提议在 IETF 中标准化该协议。后续工作包括重新定义 NXDOMAIN 区分器(出现在 NSEC 记录的类型位图字段中的“NXNAME”伪 RR),以及一种可选的、利用新的 EDNS 头部标志(Compact Answer OK)来恢复 NXDOMAIN 响应码的信令机制。
因此,紧凑的不存在证明规范要求权威名称服务器通过在 `NSEC` 或 `NSEC3` 记录中添加 `NXNAME` 信号来显式暴露域名不存在的事实。
在这个例子中,紧凑的 `NSEC` 记录证明了域名 `a\.example\.com` 不存在,因为设置了 `NXNAME` 位/类型:
``
a.example.com. 300 IN NSEC \000.a.example.com. RRSIG NSEC NXNAME
``
以下是一个不存在的域名的真实示例。请注意 AUTHORITY 部分第二条记录中的 `TYPE128` 标志,它对应 `NXNAME`。(当前稳定版本的 `dig`,包含在 BIND 9.20 中,不完全支持 `NXNAME`。)
``
~ ❯ dig doesnotexist.cloudflare.com MX +dnssec +noall +comments +authority
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 61680
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 4, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags: do; udp: 1232
;; AUTHORITY SECTION:
cloudflare.com. 291 IN SOA ns3.cloudflare.com. dns.cloudflare.com. 2407081620 10000 2400 604800 300
doesnotexist.cloudflare.com. 291 IN NSEC \000.doesnotexist.cloudflare.com. RRSIG NSEC TYPE128
cloudflare.com. 291 IN RRSIG SOA 13 2 300 20260705103523 20260703083523 34505 cloudflare.com. u6pDc961FGq0rOBLW8c7D8cjPgW3keDM16d18INAPfACEF6WcyRmr9y3 EOJfv3AT9DdvHP6HZjPF9nY6v+WpIg==
doesnotexist.cloudflare.com. 291 IN RRSIG NSEC 13 3 300 20260705103523 20260703083523 34505
相似文章
DMARC 自 2012 年公开,仍有 68.4% 的域名未强制执行
CipherCue 对 67,336 个域名的分析发现,尽管 DMARC 标准自 2012 年起就已公开,但仍有 68.4% 的域名未强制执行该协议,许多域名因难以验证所有合法邮件来源而停留在监控模式。
如果你喜欢MITM攻击,就别管DNSSEC
文章认为,忽略DNSSEC会让用户暴露在中间人攻击之下,并以电子邮件、Matrix和XMPP为例,将其与历史上对HTTPS的抵制进行类比。
将DMARC ARC重新归类为历史
这份IETF草案建议将ARC(认证接收链)协议重新归类为历史,结束其实验,并指出DKIM2作为其继任者。
ACME CAA扩展将变为强制要求
CA/Browser Forum已投票决定,ACME CAA扩展将在2027年3月前成为强制要求,这是使用DNSSEC实现强加密域名验证的关键一步。
你想要部署 FN-DSA
本文讨论了 FN-DSA 后量子签名标准的当前状态、标准化延迟以及部署中的重要注意事项,包括预哈希考虑。