跳到主要内容

Punycode 与中文域名:xn-- 前缀到底在干什么

讲清楚 Punycode 怎么把中文域名编成 xn-- 形式,为什么 DNS 离不开它,以及同形字钓鱼是怎么钻这个空子的,附中文域名转换实例。

发布于 作者 李雷
#punycode #国际化域名 #中文域名 #网络安全 #编码

Punycode 与中文域名:xn-- 前缀到底在干什么

你大概在地址栏里见过 "xn--fiqs8s" 这种长得很丑的字符串,也可能买过一个中文域名,结果发现 DNS 面板死活不收你输的中文。这两件事其实是同一回事,背后都是 Punycode。这篇文章把它讲透:Punycode 是什么,为什么非要它不可,以及它怎么被钓鱼网站利用。

DNS 只认 ASCII,这是历史包袱

域名系统(DNS)诞生于 1980 年代,那时候它只设计来处理 ASCII:26 个英文字母、10 个数字,加一个连字符,业内管这套叫 LDH(letter-digit-hyphen)规则。整个解析链路,从你电脑里的 resolver 到根服务器,几十年来都是按这个假设写的代码。

后来世界要用中文、日文、阿拉伯文、西里尔文上网,问题就来了:DNS 在网络上根本传不了 "中文.中国" 这串字节。直接改 DNS 协议代价大到不可想象,全球几百万台服务器没法一起升级。于是工程师们选了另一条路:不动 DNS,而是在域名进 DNS 之前,先把非 ASCII 字符可逆地编码成纯 ASCII。这个编码方案就是 Punycode,定义在 RFC 3492 里。

xn-- 前缀是怎么来的

Punycode 工作在 label 这一级。一个域名按 "." 切成若干段,每段叫一个 label。比如 "shop.中文.com" 有三段:shop、中文、com。只有含非 ASCII 字符的段才需要编码,纯英文的段原样保留。

每个被编码过的 label 都会加上 "xn--" 这个前缀,它叫 ACE(ASCII 兼容编码)前缀。resolver 看到 "xn--" 开头,就知道这段是编码过的,显示给人看的时候要解回原文。具体来说,Punycode 用一种叫 bootstring 的算法,带 bias 自适应,把 Unicode 码点的位置信息打包成一串 ASCII 字符,挂在前缀后面。

举个我自己注册中文域名时踩过的实例。我买下 "中文.中国" 之后,DNS 托管面板和证书签发方都不收中文,只认 ASCII。我把它丢进编码工具,得到:

中文.中国  →  xn--fiq228c.xn--fiqs8s

注意这里两段是分开各自编码的:"中文" 变成 "xn--fiq228c","中国" 这个顶级域变成 "xn--fiqs8s"。当时我图省事想把整串当一块编,结果出来一堆乱码,解析直接挂掉,连个报错都没有。后来才明白点号是 label 边界,不是要编码的字符,必须逐段处理。你可以用 Punycode / IDN 域名转换 试一下,它会自动按 "." 切分并逐段拆解,省去手动切分的麻烦。

IDN:让人看见母语,让机器看见 ASCII

把这套机制包起来对外的名字叫 IDN,国际化域名。它的核心想法很简单:人看到的是 "中文.中国",机器在 DNS 里传的是 "xn--fiq228c.xn--fiqs8s",浏览器负责在两者之间做翻译。

这就解释了一个常见困惑:为什么有时候浏览器能显示中文域名,有时候却给你看 "xn--" 这串丑东西?答案是浏览器有一套 IDN 显示策略。当一个 label 里混了多种文字系统,或者命中了已知的同形字模式,Chrome 和 Firefox 会故意把它显示成原始的 "xn--" 形式。地址栏里冒出 "xn--" 往往不是 bug,是浏览器在保护你。

同形字钓鱼:Punycode 最阴暗的那一面

浏览器为什么要这么戒备?因为很多文字系统里都有跟拉丁字母长得几乎一模一样的字符。西里尔字母 "а"(U+0430)看着就像拉丁 "a",希腊字母 "ο" 像 "o"。攻击者可以注册一个 "аpple.com",里面那个 "a" 是西里尔字母,肉眼根本分不出来,但它编码出来的 "xn--" 主机名跟真正的 "apple.com" 完全是两个域名,指向攻击者自己的服务器。

遇到一个看不懂的 "xn--" 链接时,最稳妥的做法是先解码看看底下到底是什么。比如把 "xn--pple-43d.com" 丢进解码方向,会解出 "аpple.com",前面那个就是西里尔 "а"。这个解出来的字符就是它冒充 Apple 的铁证。如果一个链接自称是某品牌、解码后却是一堆混合脚本的 Unicode,直接当它是恶意的。

我的习惯是再配合 Unicode 字符检查器 把那个可疑字符的确切码点查出来,U+0430 还是 U+0061 一目了然,然后再决定举报还是封禁。因为这两个工具都是 100% 浏览器本地运行的,你不必把攻击者的 URL 交给任何第三方服务器,排查可疑域名时这一点很重要。

用之前记住三个坑

第一,别把整个域名当一个字符串去编码。Punycode 是逐 label 工作的,点号是边界不是字符。

第二,别手改 "xn--" 后面那段载荷。"xn--" 大小写不敏感,"xn--MNCHEN-3YA" 和 "xn--mnchen-3ya" 解出来一样,因为 DNS 大小写不敏感;但你要是动了载荷里任何一个字符,得到的就是另一个或非法的域名。永远走一遍真正的编解码器。

第三,准备注册的域名,编码前先做 UTS-46 归一化。注册商会先把域名转小写、做 Unicode NFC 归一化,再编码。你直接拿 "MÜNCHEN.DE" 去编,得到的 xn-- 技术上合法,却可能跟实际注册的那个不一样。先归一化再编码,才不会对不上。

搞懂了 Punycode,你看 "xn--" 就不再是一串乱码,而是一段能读、能验、能防钓鱼的信息。


Made by Toolora · Updated 2026-06-13