仓库源文站点原文

DNSSEC 简介

看懂这篇文章可能需要的前置知识 DNS ,不对称加密,数字摘要,数字签名

DNSSEC(DNS Security Extensions) 域名系统安全扩展。 开启DNSSEC,可有效防止DNS欺骗和缓存污染等攻击。 它是通过数字签名来保证DNS应答报文的真实性和完整性。

DNSSEC 的工作原理

一般的 DNS

查询 A 记录 -> 返回 A 记录的值

启用了 DNSSEC 的 DNS

查询 A 记录 -> 返回 A 记录的值和 A 记录的 RRSIG

DNSSEC 大致的验证流程

  1. 查询 A 记录
  2. 从响应报文里获取 RRSIG 记录
  3. 从 RRSIG 记录获取 singer (singer 是一个域名,可能同一级域名也可能是上一级域名)
  4. 查询 singer 的 DNSKEY 记录,获得 ZSK 和 KSK
  5. 使用 ZSK 验证 A 记录的 RRSIG
  6. 如果结果一致,继续查询 singer 的 DS 记录
  7. 对比 KSK 的数字摘要和 DS 记录里的是否一致
  8. 如果结果一致,继续查询 singer 上一级域名的 DNSKEY
  9. 用上一级域名的 ZSK 验证 DS 记录的 RRSIG
  10. 如果结果一致,继续套娃,查询上一级域名的 DS ,直到根域名为止

让域名支持 DNSSEC

  1. 确认顶级域名支持 DNSSEC
  2. 确认域名注册商支持 DNSSEC
  3. 添加对应的记录
    1. RRSIG (Resource Record Signature) 资源记录签名 该记录用于存放当前域名每一条记录的签名
    2. DNSKEY (DNS Public Key) DNS 公钥
      • DNSKEY 会有两条记录
      • Zone-Signing Key(ZSK) 一般查询的记录由 ZSK 签名
      • Key-Signing Key(KSK) DNSKEY 记录也有 RRSIG ,由 KSK 签名
    3. DS (Delegation Signer) 授权签名
      • KSK 的数字摘要
      • DS 记录也有 RRSIG ,由上一级域名的 ZSK 签名
    4. NSEC (Next Secure) 下一个安全的 用于验证不存在的资源记录
      • 其实笔者没搞明白,这个是用来干什么的<!-- - 用于说明该域名下有哪些记录,从而可以用排除法证明该域名下没有哪些记录。 -->

检查网站对 DNSSEC 的支持

DNSSEC 记录

现在的 DNSSEC 至少有 8 种记录类型

下面几个小节主要是描述响应报文里的 Answer 的 RDATA

DNSKEY

Algorithm 的列表 https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml

rfc 4034 里明确的表示不建议使用 RSA/MD5 作为加密算法

DS

KeyTag 的生成方式

伪代码

unsigned int
keytag (
        unsigned char key[],  /* the RDATA part of the DNSKEY RR */
        unsigned int keysize  /* the RDLENGTH */
        )
{
        unsigned long ac;     /* assumed to be 32 bits or larger */
        int i;                /* loop index */

        for ( ac = 0, i = 0; i < keysize; ++i )
                ac += (i & 1) ? key[i] : key[i] << 8;
        ac += (ac >> 16) & 0xFFFF;
        return ac & 0xFFFF;
}

php

$flags = 257;
$protocol = 3;
$algorithm = 13;
$publicKey = 'mdsswUyr3DPW132mOi8V9xESWE8jTo0dxCjjnopKl+GqJxpVXckHAeF+KkxLbxILfDLUT0rAK9iUzy1L53eKGQ==';

$bin = pack('nCC', $flags, $protocol, $algorithm);
$bin .= base64_decode($publicKey);

$keytag = array_sum(unpack('n*', $bin));
$keytag += ($keytag >> 16) & 0xFFFF;
$keytag = $keytag & 0xFFFF;

Digest 的生成方式

伪代码

digest = digest_algorithm( DNSKEY owner name | DNSKEY RDATA);
DNSKEY RDATA = Flags | Protocol | Algorithm | Public Key.

php

$domainName = 'nslookup.io.';
$flags = 257;
$protocol = 3;
$algorithm = 13;
$publicKey = 'mdsswUyr3DPW132mOi8V9xESWE8jTo0dxCjjnopKl+GqJxpVXckHAeF+KkxLbxILfDLUT0rAK9iUzy1L53eKGQ==';
$publicKey = base64_decode($publicKey);

$domainNameBin = '';
foreach (explode('.', $domainName) as $part) {
    $domainNameBin .= pack('C', strlen($part));
    $domainNameBin .= $part;
}
if (substr($domainName, -1) !== '.') {
    $domainNameBin .= pack('C', 0);
}

$rdata = pack('nCC', $flags, $protocol, $algorithm);
$rdata .= $publicKey;

$data = $domainNameBin . $rdata;

$digest_algo = 'sha256';
$digest = openssl_digest($data, $digest_algo);

RRSIG

DNSSEC 的问题

  1. 只有部分顶级域名支持
  2. 即使顶级域名支持,还需要域名注册商的支持
  3. 无法保证私密性
  4. 挟持发生时不能告诉用户真正的记录
  5. 如果要让互联网变得更加安全,那么其它层面的保护(例如 TLS 证书和 DoH )同样重要。

其它

Windows 的 nslookup 不支持 DNSSEC 的记录查询。

PowerShell 的 cmdlet Resolve-DnsName 支持 DNSSEC 的记录查询。

Resolve-DnsName www.nslookup.io -type A -DnssecOk -server 8.8.8.8

最好还是用 dig 命令来检测 DNSSEC 。

dig @8.8.8.8 www.nslookup.io +dnssec

参考