域名系统在设计之初只支持 ASCII 字符,但现实中大家希望能用中文、日文等本地语言注册和访问域名。Punycode 就是让"中文.测试"这类国际化域名能在只认识 ASCII 的 DNS 系统里正常工作的编码方案。
域名系统(DNS)诞生于互联网早期,标准里域名只能使用 ASCII 字母、数字和连字符(-)。但互联网早已是全球性的,中文、日文、阿拉伯文用户都希望能用自己的语言注册和输入域名——这类使用非 ASCII 字符的域名叫"国际化域名"(IDN,Internationalized Domain Name)。问题是底层的 DNS 协议和基础设施没有为了 IDN 重新设计,怎么才能不改动 DNS 协议本身,又支持这些域名?
解决方案是 Punycode(RFC 3492 定义的编码算法):把域名里每一段包含非 ASCII 字符的标签,转换成一段纯 ASCII 字符串,并在前面加上固定的 xn-- 前缀作为标识——DNS 系统实际存储和解析的是这个转换后的 ASCII 形式,浏览器地址栏显示的则是原始的本地语言文字,两者通过 Punycode 算法互相转换。
比如中文域名"中文.测试",转换成 Punycode 后是 xn--fiq228c.xn--0zwm56d——注意域名里每一个用点分隔的标签(label)是分别独立编码的,如果某个标签本身全是 ASCII 字符(比如 www),就原样保留、不加 xn-- 前缀。Punycode 算法本身是可逆的:给定编码后的 ASCII 字符串,能唯一还原出原始的 Unicode 字符序列,这也是为什么它能作为 DNS 系统的透明兼容层——DNS 服务器完全不需要知道"这其实是个中文域名",只需要按普通 ASCII 域名处理即可。
Punycode 机制带来了一个不容忽视的安全隐患:同形异义字攻击(Homograph Attack)。不同语言的字符集里,存在大量"看起来几乎一样,但实际是不同字符"的情况——比如西里尔字母的"а"和拉丁字母的"a",肉眼几乎无法分辨,但它们是完全不同的 Unicode 码点,编码成 Punycode 后会得到完全不同的字符串。
攻击者可以利用这一点,注册一个看起来和知名网站域名几乎一样(视觉上)、但实际是用其它语言字符拼出来的域名,诱导用户误以为访问的是正规网站,从而实施钓鱼攻击。这也是为什么安全意识较高的浏览器和安全工具,遇到包含非常见字符组合的域名时会主动显示其真实的 Punycode 编码形式(xn-- 开头),或者对疑似同形异义字域名发出警告——查看域名的 Punycode 真实编码,是识别这类仿冒域名的直接手段之一。
在线工具:Punycode编码解码