打码究竟遮住了什么
2018 年,大多数域名的注册记录被清空。GDPR 生效后,ICANN 要求注册商停止在公开 WHOIS 输出中显示注册人姓名、地址、电话和邮箱,原本能查到一个人的查询开始返回 REDACTED FOR PRIVACY 和一个联系表单。看上去像是隐私保护,实际只是一项显示设置。
底层的东西一样没动。你的注册商依然保存着完整记录,依据其认证资质依然被要求保存这份记录,并且依然会交给任何有资格索取的人——法院令状、执法请求,而现实中还有远比这便宜的办法。一份 UDRP 投诉只要几千美元,不需要法官,还会触发一个注册商核实步骤,在案件裁定之前就把背后的注册人身份披露给投诉方。这是几乎没人会纳入考虑的解除匿名途径,也是商标持有人最先动用的一条。
还有两样东西在打码之后依然存在。第一是历史记录:商业存档服务对 WHOIS 做了二十年的快照,这些快照永不过期,所以一个在 2018 年之前用真实信息注册的域名,不论今天的记录怎么写,都永远处于暴露状态。第二是你自己配置的其余部分——你选的域名服务器、藏在 SOA 里的地址、DMARC 报告邮箱,以及你申请过的每一张证书。打码清空了一个字段,却留了另外四处继续公开,而这四处正是本指南大部分篇幅要处理的内容。
先选后缀,再选注册商
注册商的政策取决于注册局的政策,而注册局的政策又取决于这家注册局公司恰好在哪里注册成立。注册商可以在隐私方面许下任何承诺;但它承诺的范围不会超出注册局允许的范围,而这两者都无法对注册局所在司法辖区的法院做出任何承诺。所以顺序很重要,而大多数人恰好做反了:应该先选后缀,再选把它卖给你的人。
三个属性把各个后缀区分开来。注册局运营方所在地决定了哪国法院能下令在根一级吊销一个域名——对于那些历史悠久的大型 gTLD 而言,这个地方就是美国,这也是为什么一个持有者在美国毫无关联的 .com 依然能凭一纸美国法令被没收,也是为什么你自己所在的司法辖区远没有你希望的那么重要。注册局默认会公开什么差异极大:一些 ccTLD 注册局打码打得比 gTLD 还彻底,另一些则对任何查询者都打印出注册人姓名,还有少数要求经过核实的本地实体存在,而这一点你只能用真实身份才能满足。是否允许使用隐私服务是注册局的规则,而不是注册商的功能——少数几个后缀直接禁止代理注册,而如果某个注册商在这类后缀上卖给你隐私服务,之后也只会被强行剥离。
这些都不稳定到可以死记硬背的地步,你在网上找到的任何一份"对隐私友好的 TLD"清单,等你读到时多半已经有部分过时。正确的做法是自己查:第二步会针对你正在考虑的任何后缀,用一个你并不拥有的域名,跑一遍能回答这三个问题的查询。要在付款之前跑这些查询,因为一个选错的后缀无法靠转注册商来补救,唯一的补救办法是换一个名字重新开始。
SP·03三种注册商模式,以及各自能扛住什么
剥掉营销话术,实际能选的只有三种安排。在代理模式下,你就是注册人,注册商用自己的联系方式替代你的信息对外发布。你的数据其实早已在注册商的数据库里,打码只是盖在一份完整记录上的表面装饰,而代理服务商自己的条款几乎总会保留在收到"合理"第三方请求时披露你身份的权利。它能完全挡住爬虫和数据经纪商,但对任何带着律师上门的请求毫无招架之力。
在记录持有人模式——Njalla 让这种安排流行起来——服务商以自己的名义注册域名,再通过合同授予你使用权,你的身份因此从未成为注册人。注册局里没有什么可披露的,因为那里根本没有相关信息。这笔交易是真实存在的,也值得说清楚:你在法律上并不拥有这个域名。你依赖的是这家公司的偿付能力、它抵抗请求的意愿,以及它对这门生意的持续兴趣。一旦它倒闭、被收购,或者认定你带来的麻烦超过了价值,你能诉诸的只是一场围绕着登记在别人名下的资产的合同纠纷。
第三种安排是拥有一个干净的注册人身份:用一个没有历史的用户名、一个只为此用途存在的邮箱、以加密货币支付,在一个与你的主机和你本人都毫无关联的司法辖区的注册商那里完成真实注册。它能扩展到不止一个域名,不依赖任何一家公司的善意,也是本指南接下来要搭建的方案。它同样是最经不起马虎的一种,因为一切都建立在这个身份永远不会在别处与你产生关联的前提上——身份归属指南逐层拆解了这种失效模式。顺带一提,这两种模式结合得很好:用一个干净身份购买的记录持有人注册,比单独使用其中任何一种都更牢固。
SP·04注册表单:哪些信息能拒填,哪些会让域名被暂停
表单会要求填写姓名、通讯地址、电话号码和邮箱。其中三项是注册商依规必须保存、但几乎从不核实的合同数据。只有一项是承重的。真正要紧的是邮箱:根据注册商认证协议,注册人邮箱必须在注册后完成验证,如果没人点击验证链接,域名就会被暂停——通常在十五天以内。一个用完即弃、后来不复存在的邮箱,会把域名一起拖下水。
所以这个地址必须是一个你真正掌控、三年后依然会去查看的别名邮箱,其提供商既不要求手机号开户,也不会因为长期不用而关闭账号。表单上的其余信息,考验的是一致性而不是真实性:选定一套说得过去的信息,加密保存,并且在注册商那里、在账户资料里、在你日后打开的任何一张支持工单里,都使用完全相同的信息。不一致才是把账户送进人工审核的原因,而人工审核正是有人开始索要证件的地方。
然后用一条不会把法定姓名带入记录的支付通道付款。不管 WHOIS 上写了什么,用银行卡都会让整套操作前功尽弃,因为支付处理商的记录比注册记录活得更久,也极容易被传票调取;匿名支付指南按各种方式各自会泄露什么做了排序。如果注册商直接支持门罗币,那是最干净的路径。如果只支持比特币,就把这笔付款当成永久可追踪的,并据此安排资金来源。如果两者都不支持,那么这家注册商已经用行动告诉了你,它想要什么样的客户。
SP·05DNS 是第二次注册
你一发布区域文件,就等于把自己重新公开了一遍,而且是在没人当作身份信息的字段里。SOA 记录里带着一个邮箱地址,编码方式是用它的第一个点代替 @,而手写的区域文件里那往往就是一个真实地址。域名服务器名称是一种指纹:把两个域名指向同一组自定义域名服务器,你就永久地把它们关联在了一起,而且这份关联会被反向域名服务器搜索引擎收录并保存。而当这些域名服务器本身就位于该域名之内时,注册局还会公开胶水记录——它们的 IP 地址,写在根区域里,是你放在网站前面的任何 CDN 都够不着的地方。有人花一星期时间隐藏源站,结果又在一条胶水记录里把地址交了出去。
DNSSEC 值得专门提醒一句,因为它被当作一项安全功能来卖,表现出来的却是一项披露功能。用 NSEC 签名一个区域,会把其中每一个名字发布成一条可被任何人在几秒钟内遍历完的链表。NSEC3 改用哈希处理名字,听起来更好,但只是把破解成本提高了一些:这些哈希可以离线破解,而且有专门只做这件事的工具。如果你想要经过认证的应答,就去签名,但要清楚你并没有因此隐藏子域名列表——你从来就没有隐藏过。
区域文件在哪里托管,是一个和谁注册了这个名字彼此独立的决定,把两者分给两个国家的两家公司来做,比单独优化其中任何一个都更值。自己运行域名服务器换来的是控制权和一个不起眼却有辨识度的指纹;使用大型服务商换来的是隐没在人群中的匿名性,以及一家会记录每一次查询、并会就此回应请求的公司。这里没有免费选项,只有一个你更愿意留下哪种记录的选择。如果区域文件背后的网站部署在某个边缘节点之后,源站 IP 指南会讲完这条链路剩下的部分。
SP·06注册完成之后才出现的泄露点
证书透明度是最大的一个。每一张受公开信任的证书都会被写入一份公开、只能追加、可永久搜索的日志,所以你曾经让 CA 签发过的每一个名字都可以被发现——包括你以为没人知道的 staging.、vpn. 和 old- 开头的主机,也包括你有一次不小心签发、当天下午就删掉的内部名称。日志条目无法撤回。唯一能控制的地方在上游:改用基于 DNS-01 验证的通配符证书,让具体主机名根本不会进入日志,并把真正私密的名字留给一个什么都不记录的内部 CA。
邮件还会再暴露两处。SPF 记录会指名你用来发信的中继。DMARC 记录通常会暴露一个真人,因为 rua= 就是报告的送达地址,而人们习惯把自己真实的邮箱填在那里。而一条同时指向承载网站那台机器的 MX 记录,只要有人跑一次查询,就会把源站地址奉送出去——邮件是源站从边缘节点背后暴露出来的最常见途径,没有之一。
最后一类是关联性,它能把原本处理得很仔细的工作前功尽弃。同一组域名服务器出现在两个身份下,就把它们关联在了一起。同一张覆盖两个名字的证书、同一个统计分析标识符、同一个 favicon——互联网范围的扫描器会专门收录 favicon 哈希,正是因为它们的指纹辨识度太高——同一个不寻常的响应头、同一个夜晚同一个时间点使用同一家注册商,效果都一样。这些线索单独拿出来都是弱证据,三条凑在一起就不是了。第四步和第六步会针对你自己的域名,跑一遍对手会跑的那些查询,这是了解自己到底公开了什么的唯一诚实方法。
SP·07续费、转移,以及第三年才会出现的失败
匿名域名很少死于一张传票,它们死于注册过期。别名邮箱不再有人查看,续费通知被退回,余额耗尽,一个花了一年时间才养起来的域名掉进了池子里,而抢注机器人早就等在那儿了。打开自动续费,在注册商那里留足够覆盖好几年的余额,并把到期日纳入你真正会去看的提醒机制——第七步会直接从注册局把到期日拉出来,这样提醒就不必依赖那个最可能出问题的邮箱。
锁定是另一半,而且是免费的。clientTransferProhibited、clientUpdateProhibited 和 clientDeleteProhibited 各自只需点一下,就能封死那一整类始于有人闯入你注册商账户的攻击。有些注册局还提供注册局级锁定,任何变更之前都需要带外确认,对一个你输不起的域名来说,这笔钱花得值。把转移授权码加密后离线保存,并且要清楚更改注册人联系方式会触发六十天的转移禁止期——所以这类整理工作要挑不赶时间的时候做,绝不要在事故进行中做。
然后再为你本人无法到场的那种情况做打算。一个以匿名身份持有的域名没有遗产认证程序,没有支持升级通道,除了凭证本身也没有任何所有权证明,这就使得一份加密保存的账户信息、恢复代码和授权码副本——存放在你信任的人最终能够取用的地方——成了一个长期项目和一个死掉的域名之间唯一的分界线。同样的逻辑也适用于它背后的那台机器:一份没人能恢复的异地加密备份,等于没有备份。
SP·08一个现实的威胁模型
要精确地说清楚这套方案能防住什么。面对爬虫、数据经纪商、竞争对手、开源情报研究者,以及那种从你的一个域名顺藤摸瓜找到其余所有域名的机会主义者,一个搭配无泄露区域文件的干净注册人身份是有效的,而且是彻底有效——公开记录里没有任何东西可供顺藤摸瓜。面对民事索赔人,它把成本从一次免费查询提高到了一场法律程序,这是一个真实存在、往往具有决定性的区别。面对商标持有人提起的 UDRP,代理模式几乎帮不上什么忙,而记录持有人模式给你的,是一家必须自己决定愿意替你抗争到什么程度的公司。
面对对你的注册局拥有管辖权的国家,它赢不了,任何一种注册商安排都赢不了:不管域名握在谁手里,都可以在根一级被吊销。这恰恰是应该慎重选择后缀、并且要把自己的内容掌控到足以随时搬走的理由,而不是指望找一个更聪明的注册商能解决的问题。而面对你自己的失误,它什么都提供不了。用个人浏览器配置文件登录一次、用你平时的地址、以你平时的语气写一张支持工单、一张同时覆盖两个身份的证书——本指南教你花心思维护干净的那份记录,一个下午就能被重新拼回原样。
要针对你真正面对的对手来构建方案。对大多数人来说,诚实的答案就是数据经纪商和机会主义者,把第五步和第六步里的记录关闭掉就够了。如果你的风险敞口更高,要记住域名只是若干层里的一层:把它和一个从不过问你是谁的主机、一台从第一天起就做好加固的机器,以及不带姓名的支付方式搭配起来。隐私是一条链条,这里的每一篇指南讲的都是同一条链条上不同的一环。
SP·09分步操作
-
01
审计这个域名目前已经公开了什么
从你已经拥有的域名开始,或者从你正准备向别人购买的域名开始。RDAP 是取代了 gTLD 端口 43 WHOIS 的机读注册查询服务,它通过 HTTPS 传输 JSON,
rdap.org会帮你引导到正确的注册局。许多 ccTLD 依然只在端口 43 上应答,其中不少公开的信息比 gTLD 还多。# gTLDs: RDAP is the authoritative registration record curl -s https://rdap.org/domain/example.com | jq '{ status, ns: [.nameservers[]?.ldhName], events: [.events[] | {(.eventAction): .eventDate}], roles: [.entities[]?.roles[]?] }' # the fields that are supposed to be blank -- read them, do not assume curl -s https://rdap.org/domain/example.com \ | jq -r '.. | objects | select(has("vcardArray")) | .vcardArray[1][] | select(.[0]=="fn" or .[0]=="email" or .[0]=="adr") | "\(.[0]): \(.[3])"' # ccTLDs: many are port-43 only, and less redacted than you expect whois example.de | grep -viE '^%|^$'如果查出来的结果里带着真实姓名、真实地址或个人邮箱,就此打住:转移域名并不会抹掉这些信息,因为存档服务已经留了底。这个域名从隐私角度已经废了,诚实的做法是注册一个全新的域名并做重定向。
-
02
在敲定后缀之前先测试它
用实测来比较候选后缀,而不是看某篇博客文章的说法。在每个候选后缀下查一个你并不拥有的域名,看看注册局实际返回了什么,再核实这家注册局由谁运营、位于哪里。不同注册局返回的输出格式各不相同,这正是为什么这一步值得亲自跑一遍,而不是靠猜。
# what does each registry publish about a registrant? for d in example.com example.de example.is example.nl; do printf '\n== %s\n' "$d" curl -s "https://rdap.org/domain/$d" \ | jq -r '.entities[]? | "\(.roles|join(",")): \( [.vcardArray[1][]? | select(.[0]=="fn")][0][3] // "redacted")"' \ 2>/dev/null || whois "$d" | grep -iE '^(registrant|owner|admin-c)' done # who runs the registry, and under whose law does that company sit? curl -s https://www.iana.org/domains/root/db/is.html \ | sed -n 's/.*Organisation:*//p;/Registry Information/,+6p' | head -20三个答案能决定取舍。注册局是否默认公开注册人姓名?是否要求经过核实的本地实体存在?哪国法院能管到这家注册局的运营方?任何要求本地实体存在的后缀,除非你能如实满足,否则出局;任何运营方位于你特意想要避开的司法辖区的后缀,不管注册商本身看起来多好,同样出局。
-
03
在开户之前先把身份构建好
一次性把身份生成好,不要用明文记下任何内容。用户名不应带有任何历史痕迹——不能是你在别处用过的昵称,也不能是对你有特殊含义的词。邮箱应该是一个别名地址,提供商不要求手机号,而且要能扛住三年无人问津,因为注册商的验证邮件和每一次续费通知都会发到这里。
# a handle with no history and no meaning head -c 10 /dev/urandom | base32 | tr -d '=' | tr 'A-Z' 'a-z' # a key for the secrets this account is about to hand you age-keygen -o registrar.key # public key is printed on stderr # store profile details, recovery codes and (later) the auth code age -r age1... -o registrar.age registrar.txt && shred -u registrar.txt # read it back only when you need it age -d -i registrar.key registrar.age
使用一个从未接触过个人账户的浏览器配置文件,并且每次都通过同一条网络路径访问注册商——在不同会话之间混用家庭网络和 VPN,比始终如一地只用其中一种更容易被识别。把你提交的资料细节原样记录下来;两年后你还需要在一张支持工单里一字不差地把它们复述一遍。
-
04
完成注册,然后像陌生人一样审视自己
注册域名,当天就点击验证链接,然后等记录同步到注册局之后再去查——真正有意义的输出会在几小时后才出现,不是立刻就有。你要找的是任何没有被打码的字段,以及能告诉你验证是否真正完成的状态码。
# the public record, 24h in curl -s https://rdap.org/domain/example.com | jq '{status, events}' # expect: "active" -- NOT "pendingVerification" or "clientHold" # anything at all that survived redaction curl -s https://rdap.org/domain/example.com \ | jq -r '.. | objects | .vcardArray? // empty | .[1][] | select(.[0]=="fn" or .[0]=="email" or .[0]=="adr" or .[0]=="tel") | "\(.[0]): \(.[3])"' # and the archive view: does this name have a past you did not buy? curl -s 'https://crt.sh/?q=example.com&output=json' \ | jq -r '.[] | "\(.not_before) \(.name_value)"' | sort -u | head -20一周之后如果状态还是
clientHold或pendingVerification,说明验证邮件根本没送达,或者送达了也没人点,域名正处在暂停倒计时里。优先处理这个问题——这是一个正确注册的匿名域名在第一个月里最常见的丢失方式。 -
05
发布一份不会暴露你身份的区域文件
接下来是泄露最多、却最少被检查的部分。像陌生人那样审视你自己的区域文件:
SOA里的地址、域名服务器名称、注册局代你公开的胶水记录,以及是否有人能直接把整份区域文件下载下来。# SOA -- the second field is an email, first dot standing in for @ dig +short SOA example.com # ns1.example.com. hostmaster.example.com. 2026090601 7200 3600 1209600 3600 # ^-- must not be a personal address # glue: if your nameservers live inside the domain, the ROOT holds their IPs dig +norec +short NS example.com @a.gtld-servers.net dig +norec +short ns1.example.com A @a.gtld-servers.net # can anyone download the entire zone? dig AXFR example.com @ns1.example.com | head # want: "Transfer failed" -- anything else is your full host inventory # pin issuance to one CA, and give abuse somewhere impersonal to land dig +short CAA example.com # 0 issue "letsencrypt.org" # 0 iodef "mailto:abuse@example.com"
按以下顺序修复:把
SOA里的地址换成域名自身下的一个角色邮箱,把域名服务器搬到域名之外(或者干脆搬出你自己的机器),这样就不需要胶水记录了;拒绝除你的辅助服务器之外的任何AXFR请求;再加一条CAA记录。如果你用 DNSSEC 签名,选用 NSEC3,并且要接受一个事实:只要有人愿意花一小时,你的子域名列表依然是可枚举的。 -
06
堵上证书和邮件方面的泄露
有两份公开数据集掌握的基础设施信息,比你的 DNS 还多。证书透明度知道你签发过的每一个主机名;你自己的邮件记录则暴露了你的中继、你的报告邮箱,还常常暴露你的源站。把这两者都拿自己过一遍。
# every name you have ever asked a CA to sign, including deleted ones curl -s 'https://crt.sh/?q=%25.example.com&output=json' \ | jq -r '.[].name_value' | tr ' ' '\n' | sort -u # the mail records, read for identity rather than deliverability dig +short TXT example.com # SPF include: names your relay dig +short TXT _dmarc.example.com # rua=mailto: usually names a human dig +short MX example.com # an MX on the origin IS the origin # does the site answer on its own address, ignoring the edge? curl -sI --resolve example.com:443:203.0.113.10 https://example.com/ | head -1
把以后的证书签发都换成基于 DNS-01 验证的通配符证书,让主机名不再进入日志;把
rua=指向域名下的一个地址,而不是个人邮箱;再把邮件从源站上搬走——用一个中继或一台独立的机器,绝不要用跑网站的那台机器。已经进了日志的名字撤不回来,要么让它们退役,要么接受它们将永远公开这个事实。 -
07
锁定它、监控它,并为你不在场的情况做打算
最后一步:让这份注册记录难以被转移,也难以被遗忘。三个客户端锁是免费的,能挡住整整一类始于有人进入你注册商账户的攻击。到期日应该出现在你的告警系统里,而不是一个你可能不再查看的邮箱里。
# what the registry says about locks and dates curl -s https://rdap.org/domain/example.com \ | jq -r '.status[], (.events[] | "\(.eventAction) \(.eventDate)")' # want: clientTransferProhibited, clientUpdateProhibited, clientDeleteProhibited # days until expiry, straight from the registry -- no email involved exp=$(curl -s https://rdap.org/domain/example.com \ | jq -r '.events[] | select(.eventAction=="expiration") | .eventDate') echo $(( ( $(date -d "$exp" +%s) - $(date +%s) ) / 86400 )) days left # keep the escape hatch encrypted and off the machine it protects age -r age1... -o auth-code.age auth-code.txt && shred -u auth-code.txt把这项到期检查放进 cron 运行,在还剩九十天时发出告警。把加密打包好的账户信息、恢复代码和转移授权码,存放在你信任的人最终能够取用的地方,因为匿名域名没有遗产认证程序,也没有支持升级通道。然后每年重新跑一遍第四步到第六步:区域文件会逐渐跑偏,证书会被健忘的人重新签发出来,你一月份堵上的那个泄露点,往往到了秋天又会冒出来。


