메뉴
HN
Hacker News 23일 전

DMARC 새 'np' 태그가 DNSSEC과 충돌하는 이유

IMP
7/10
핵심 요약

최근 발표된 DMARC 표준(RFC 9989)에 새롭게 도입된 'np'(존재하지 않는 하위 도메인 정책) 태그가 DNSSEC의 한 형식(RFC 9824)과 충돌하여 예상대로 작동하지 않는 문제가 발견되었습니다. 이는 Cloudflare, AWS 등 주요 DNS 서비스를 사용하는 도메인에 영향을 미칠 수 있어 이메일 보안 관리자들의 주의가 필요합니다.

번역된 본문

← 블로그 (최종 업데이트: 2026년 7월 5일)

DMARC의 새로운 "np" 태그가 DNSSEC으로 인해 실패할 수 있는 이유 Matteo | 18분 분량

최근 업데이트되어 RFC 9989로 발표된 DMARC 명세에는 새로운 np 태그가 도입되었습니다. 이 태그의 목적은 DMARC 레코드가 게시된 도메인의 하위 도메인(Subdomain) 중 실제로 존재하지 않는 도메인으로부터 메일이 올 때, 수신자가 적용해야 할 정책을 명시하는 것입니다.

우리는 RFC 9989에 포함된 '존재하지 않는 도메인(non-existent domain)'의 정의가 "DNSSEC의 간결한 부재 증명(Compact Denial of Existence in DNSSEC)"으로 알려진 또 다른 최신 명세인 RFC 9824와 충돌하여, np 태그가 항상 기대했던 대로 작동하지 않는다는 사실을 발견했습니다.

DNSSEC의 사용이 아직 보편화되지는 않았지만, 이 문제는 Cloudflare, NS1, AWS Route 53, Azure와 같은 주요 DNS 제공업체와 함께 DNSSEC을 사용하는 모든 도메인에 영향을 미칩니다. 우리는 DMARC를 담당하는 IETF 워킹 그룹에 이 문제를 제기했으며, 문제는 인정받았지만 합의된 해결책은 아직 없습니다. 이 글에서는 전체적인 상황을 설명하고 이 비호환성이 미치는 영향을 평가해보고자 합니다.

새로운 np 태그

2026년 5월, IETF는 세 개의 문서로 구성된 최신 버전의 DMARC 명세를 발표했습니다. RFC 9989는 '존재하지 않는 하위 도메인 정책(non-existent subdomain policy)'을 의미하는 np라는 새로운 DMARC 레코드 태그를 도입했습니다. 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을 반환할 수 없습니다. 대신 빈 응답 섹션과 함께 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에 대한 보안 확장으로, 리졸버(resolver)가 DNS 응답이 진짜이며 변조되지 않았음을 검증할 수 있게 해줍니다. 이는 암호화 서명을 추가하여 수행됩니다...

원문 보기
원문 보기 (영어)
← Blog Last updated Jul 5, 2026 Why DMARC's new "np" tag can fail with DNSSEC Matteo 18 min. read The recently updated DMARC specification, published as RFC 9989, introduces the new np tag. Its purpose is to specify the policy that receivers should apply when the sender domain is a non-existent subdomain of the domain where the DMARC record is published. We discovered that the definition of “non-existent domain” contained in RFC 9989 clashes with another recent specification, RFC 9824, known as ” Compact Denial of Existence in DNSSEC ”, resulting in the np tag not always working as expected. Although DNSSEC usage is far from being widespread, the issue affects all domains using DNSSEC with major DNS providers like Cloudflare, NS1, AWS Route 53 and Azure . We raised the issue with the IETF working group responsible for DMARC: the issue was acknowledged but no solution was agreed upon. In this article, we’ll explain the whole story and try to assess the impact of this incompatibility. The new np tag In May 2026, the IETF published an updated version of the DMARC specification, consisting of three documents. RFC 9989 introduces a new DMARC record tag called np , standing for non-existent subdomain policy . In a DMARC record, it looks like this: v=DMARC1; p=none; sp=quarantine; np=reject; The policy specified by the p tag applies to the domain where the record is published, the sp tag applies to existing subdomains of the policy domain that do not publish their own DMARC record, while the np tag applies to non-existent subdomains. Setting different policies is helpful when you want to “block” malicious email on unused subdomains while keeping a less strict policy on the other subdomains. Non-existent subdomains in the DNS The DMARC RFC defines non-existent domains as follows: For DMARC purposes, a non-existent domain is consistent with the term’s meaning as described in RFC8020 . That is, if the response code received for a query for a domain name is NXDOMAIN, then the domain name and any possible subdomains do not exist. This is a common definition, nothing surprising. DNS servers typically return the NXDOMAIN response code to signal that the queried domain name and all its subdomains don’t exist, meaning that they do not have any DNS record associated with them. Here’s an example: ~ ❯ 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 A different situation is when the specific resource record type that was requested is not found, but other record types for the queried domain exist. In that case, the DNS name exists so the server can’t return NXDOMAIN . It would instead use NOERROR with an empty answer section, also known as NODATA . (A previous experimental extension to DMARC, RFC 9091 , specified that non-existent domains are those where there is an NXDOMAIN or NODATA response for A, AAAA, and MX records, explicitly mentioning that this is a broader definition than that in RFC 8020. This definition was changed when it was incorporated into RFC 9989.) In the above example, we can determine that the non-existent-subdomain.rai.it name and all its subdomains don’t exist. On the other hand, in the following example we can determine that the domain news.rai.it doesn’t have MX records, but it exists because it has other record types: ~ ❯ 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 In the DNSSEC world, things get a bit more complicated. DNSSEC in short DNSSEC is a security extension for DNS that lets resolvers verify that DNS answers are authentic and haven’t been tampered with. It does so by adding cryptographic signatures that resolvers can verify. In the following example we explicitly ask the DNS resolver to return DNSSEC-related data. We can see that the DNS response contains an additional record in the ANSWER section, with type 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== The RRSIG record contains the signature that the resolver verifies. The verification process involves querying other records in the zone ( DNSKEY ), and in the parent zone ( DS ), moving upward in the DNS hierarchy until the root zone is reached. Denial of existence in DNSSEC In standard DNS, queries for non-existent domains produce replies with an empty answer section, as we’ve seen above. This doesn’t play well with DNSSEC because an empty ANSWER section contains no records, so there’s nothing to sign . DNSSEC solves this using the NSEC record type. Instead of signing the non-existent name, it identifies an existing name coming before the requested name and reports the next existing name in canonical DNS order, proving that the requested name does not exist between them . The NSEC record is then signed so that any alteration can be detected. The DNS response code remains NXDOMAIN . For example, a zone may contain the following domain names: a.example.com z.example.com When we query domain f.example.com , which doesn’t exist, the response will say: a.example.com 300 IN NSEC z.example.com. A RRSIG NSEC It confirms that f.example.com doesn’t exist by letting us know that a.example.com exists and that the following domain in the canonical DNS ordering is z.example.com , with record types A , RRSIG and NSEC . Nothing else exists in between. In practice, it’s slightly more complicated than this, because we also need a second NSEC record to prove that no wildcard record exists that would have covered f.example.com . One exception is when we want to say that the domain name exists, just not with that record type. In this case, we can use just one NSEC record , as above, and list which record types exist. Nowadays, many DNS servers use a variant of NSEC called NSEC3 , which uses hashed names instead of the original names to make it harder for attackers to “walk” the DNS zone and enumerate all the domain names contained in it. For this to work, we then need a third NSEC3 record to prove that the “closest encloser” exists . In the worst case we’re now at 8 records in total ( SOA + SOA RRSIG + 3x NSEC3 + 3x NSEC3 RRSIG ), just to prove that a domain doesn’t exist. In some cases, this can approach the size limit of DNS UDP packets, potentially causing truncated responses and a switch to 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== dqj11