IDN 同形字检查器

两个名字可以用不同的字符写出,读起来却一样。apple.com 和 аpple.com — 其中首字母是西里尔小写 а — 在网络看来是两个不同的名字,在读者看来却是同一个词。本页把一个名字拆开,告诉你它属于哪一种:把名字分成标签、把每个 xn-- 标签解码回它承载的字符、按 DNS 限制清点字节数、指出每个形近字符及其借用的文字系统,并把整个名字折叠到它模仿的形状上。它以同样的详细程度报告查到的与没查到的,所以一份“干净”的答案意味着检查确实运行并通过,而不是什么都没查。
全部处理都在您的浏览器本地完成 — 您输入的名字永远不会被上传,也不会被解析。

初次接触? 用平实的语言解释 IDN 同形字 — punycode 能做什么不能做什么、浏览器为何有时显示 xn--,以及为什么本页是一组检查而不是一份判决。
把输入读取为: 先把名字折叠到 NFC
读作 —
DNS 形式(解析器得到的) —
大小 —
还没有检查任何东西。在上方输入或粘贴一个名字 — 一次一个 — 或按载入示例。本页可以读裸名字、从 URL 里粘出来的名字或整个 URL,并告诉您它读到的是哪一种。它运行的每项检查都列在结果下方,包括一无所获的那些。
什么是 IDN 同形字?
同形字(homograph)是一种写法不同、读法相同的词。在域名里,这个把戏通常只花一个字符:apple.com 是七个拉丁字母,而 аpple.com 以 U+0430 开头 — 西里尔小写字母 а,在您所有字体里它都与拉丁 a 画得一模一样。在网络看来这是两个不同的名字;在您的眼睛看来这是同一个词,而其中只有一个属于 Apple。

DNS 的头二十年里名字只能用 ASCII,这也是为什么关于域名的一切都以字节和字母计:标签由字母、数字和连字符组成,每个标签最多 63 字节,全名最多 255 字节。国际化域名(IDN)就是把规则向 Unicode 其余部分开放后的产物,而 punycode(RFC 3492)是让旧机制继续运转的拼写规则:含任何 ASCII 之外字符的标签写成 xn-- 标签 — 一个以折叠形式承载相同字符的 ASCII 字符串。中文.com 在网络上传输为 xn--fiq228c.com,两种拼写指向同一台主机。

这里真正要紧的是 punycode 不做什么。它只转换拼写,别的什么都不管。它不裁决 аpple.com 是否该被允许与 apple.com 并存,也没法问它,因为那个问题关乎眼睛和字体,而非字节。注册局决定卖哪些字符,浏览器决定如何显示它认为安全的名字,读者决定是否点击。本页回答的是三者之下的那个问题:这个名字究竟由哪些字符组成,以及由此它拼出的是什么?

本页针对的用例

下面的每一行都在页面加载时由本页运行一遍,再由自检按钮运行一遍,且页面必须报告与该行所说完全一致的内容 — 相同的发现、相同的级别、相同的 DNS 形式、相同的字节数,不多不少。这正是这里一份“干净”答案有分量的原因:放行普通名字的代码路径,与抓出下列花招的是同一条。

用例是什么 级别 到达时的名字 页面必须报告的内容

本页如何判断
  1. 从粘贴的内容里取出名字。裸名字按原样使用;URL 则取出其中的主机名,丢弃协议、端口、路径和查询,并且页面会说明它做的是哪一种,而不是悄悄把斜杠当作名字的一部分来分析。
  2. 按客户端的方式清理它。控制字符、零宽字符和字节序标记会被移除,全角句号和表意句号算作点,末尾的点按根域处理而不是错误。这样被移除或被重新解释的每个字符都会报告,因为一个被悄悄丢掉的零宽空格恰恰是您需要被告诉的事。
  3. 拆成标签并逐一测量。标签不允许为空、不允许以连字符开头或结尾、也不允许在第三四位带两个连字符(除非是 xn-- 标签)。每个标签按 UTF-8 字节对照 63 字节上限测量,全名对照 255。
  4. 把每个 xn-- 标签解码回它的字符。解码后的标签会被再编码一次并与您的输入比较。经不起这个往返的标签就不是它自称的那个标签;解码出控制字符或解码为空的标签会被如实报告,而不是被悄悄丢弃。
  5. 问每个字符是什么。它的码点、它的 UTF-8 字节、它所属的文字、它是否落在一组画法相近的字符里、以及那组的其他成员是什么。正是在这里,一个西里尔 а 不再是“一个字母”,而变成“一个形状像拉丁字母的字母”。
  6. 把名字折叠到它模仿的形状。每个形近字符都被替换成它所像的字符,于是 аpple.com 变成 apple.com。这个折叠读法是全页最有用的一行,因为 apple.com 是您可以据此行动的东西,而“一个西里尔 а”不是。
  7. 汇总发现并报告最坏的一条。每条发现只报告一次,并附其出现的标签。只是看起来不寻常的名字产生的是备注(notes)而不是发现:汉字与假名混用,或以根点结尾,都是普通的书写方式,而不是值得担忧的理由。

级别及其含义

none 这里没什么值得提的 — low 同一个名字的另一种写法 — medium 一个 DNS 无法承载的字符,或一个折叠后变成普通字母的字符 — high 一个刻意冒充别的名字的名字,或根本无法发送的名字。

级别是这些发现最坏能意味着什么,不是概率,也不是对网站的评判。一个完全诚实的名字可能是 medium,只因它含一个主机名字母表之外的字符;一个恶意名字可能仅凭一个西里尔字母就是 high。级别下方的列表逐条说明原因,一行一个发现,那才是值得读的部分。

它运行的检查,逐项列出

下表与本页报告所用的是同一份清单,所以这一行与结果里的发现不可能互相矛盾。什么都没查到的检查会列为 no:这张表的意义就在于您能看到每个问题都被提出并回答,而不是去猜它到底查没查。

检查 最坏意味着什么 它查找什么

同一份清单也驱动备注(notes),备注不带级别:只有一个标签、末尾的根点、非 ASCII 的顶级域、非点分隔符、大写名字、清理时被移除的字符,以及被归约成主机名的 URL。

DNS 强制的限制

名字是一串标签,每一个都按字节而不是按字符计数。这个区别正是 punycode 存在的全部理由:中文 是两个字符、六个字节,它的 xn-- 形式是十五个字符,所以必须放得下的是编码后的拼写。本页同时套用两条上限,并报告您撞上的是哪一条。

限制 值 到达边界时会发生什么
为什么浏览器有时显示 xn--

您可能注意过,用中文或西里尔文写的名字有时会以 xn-- 乱码的形式出现在地址栏里,而不是您期望的字符。这是浏览器拒绝显示漂亮形式,不是网站注册失败。浏览器对从未见过的名字会套用它自己的一条规则,而这条规则的存在理由正是本页的全部主题:把 аpple.com 显示为 аpple.com 正是攻击者想要的东西,所以一个陌生的混合文字名字会以编码拼写显示,直到它被见过足够多次而获得信任。

名字出现的位置 显示的内容 为什么选它

本页不会做什么
这是一组检查,不是判决,也不是黑名单。它报告名字由什么构成;它不知道网站做什么。一个名字可以通过这里的全部检查而仍是钓鱼页;一个名字可以被标记为混合文字却是一个完全普通的网站,属于用自己的语言书写的人。形状组是 UTS #39 混淆字符集的手写子集,所以本页不认识的字符对它是不会报告的。而且这里的一切都不做解析、下载或查询:您输入的名字从不离开浏览器,这也意味着本页无法知道一个名字是否存在。
规则出自何处

Punycode 是 RFC 3492。本页的自检把该文档印出的十一个示例标签作为第一项检查,所以这里的编码器以标准中的示例为准绳,而不是以自己为准绳。IDN 要经过的处理规则是 RFC 5891 及其字符表 RFC 5892,浏览器实际套用的兼容层是带 non-transitional 标志的 UTS #46;本页告诉您这些规则会做什么,而不假装实现了它们。本页实际执行的折叠 — 先 NFC、再小写、再 RFC 3492 — 与客户端在发送名字前执行的短序列相同。这一族页面的形状组、文字范围和不可见字符表是为本站手写的;列出标准名是为了您能自己阅读,这里没有复制任何标准原文。

问答

混合文字的名字一定是攻击吗?

不是,而且那样对待它会错得有实际影响。日语在同一个词里混用汉字和假名,韩语在谚文里夹用汉字。没有通常解释的是同一个标签里拉丁字母与西里尔或希腊字母混用,因为这些文字在任何语言里都不并用,本页画的就是这条线,而不是把一切混合都标记出来。这正是上面用例表中日语和韩语行都返回干净的原因。

那为什么我自己的名字返回 medium?

最可能是因为它含一个主机名字母表不包括的字符 — 下划线、空格、写成兼容字符的变音符号 — 所以是 medium,因为这个名字按原样无法发送。medium 的意思是“需要看一眼”,不是“这是伪造”。级别下方的发现会指出确切的字符及其位置。

本页会告诉我一个名字是否危险、是否已注册、是否可达吗?

三个都否。它从不接触网络,也不解析任何东西,所以无从知道一个名字是否存在或归谁所有,而且它刻意不对网站的所作所为发表意见。它回答的是一个更窄、您可以自行验证的问题:这个名字由哪些字符组成;如果把形近字折叠到它们模仿的字母上,它拼出的是什么?

我的名字是中文的,页面说顶级域不是 ASCII。出问题了吗?

没问题。中文顶级域以它自己的 xn-- 标签传输,所以名字的最后一个标签以其编码形式书写,页面会记下这一点。备注不带级别,因为它们陈述的是事实而非问题。

为什么“读作”一行有时显示的正是我输入的名字?

因为只有当折叠确实改变了什么时,本页才显示折叠读法。如果名字里的每个字符本来就已经是它模仿的形状,折叠读法与名字就是同一个字符串,也就没什么可告诉您的。

什么是 A 标签和 U 标签?

A 标签是用于传输的 ASCII 拼写:xn--fiq228c。U 标签是同一标签以所属文字写出的形式:中文。它们是同一个标签的两种拼写,本页对每个解码的标签都同时显示两者,让您看到编码标签藏起了什么。