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 標籤是同一標籤以所屬文字寫出的形式:中文。它們是同一個標籤的兩種拼寫,本頁對每個解碼的標籤都同時顯示兩者,讓您看到編碼標籤藏起了什麼。