開發者工具 · URL 編碼器和解碼器
Punycode 與百分比編碼:如何處理非 ASCII 域和路徑
· 背景
國際化 punycode url 編碼
具有非 ASCII 主機名稱和非 ASCII 路徑的 URL 使用兩種完全不同的編碼。這篇文章解釋了主機的 IDNA 和 punycode、其他所有內容的百分比編碼,以及為什麼會有這種拆分。
在一個瀏覽器中顯示“münchen.example”,在另一個瀏覽器中顯示“xn--mnchen-3ya.example”的地址 — 一個主機,兩種拼寫
慕尼黑市出現在德國域名。在瀏覽器的網址列中,您可能會看到 münchen.example 正常顯示。從另一個應用程式複製地址,它顯示為 xn--mnchen-3ya.example,這是一個純 ASCII 字串,看起來與德語文字完全不同。一個 URL,兩種拼寫,都絕對有效。兩者都沒有錯;它們使用完全不同的字元集表示相同的域。這種差異反映了 DNS 運作方式以及網際網路基礎架構如何傳輸主機名稱的基本限制。
像 /café/ 這樣的路径段需要編碼,但使用不同的系統。非 ASCII é 在路径中變為 %C3%A9。為什麼有差別? DNS 限制要求主机名使用 punycode。
為什麼主機名稱不能使用百分比編碼 - DNS 標籤、允許的字元和長度限制
DNS 標籤(由點分隔的主機名稱的各個部分)具有非常嚴格的規則。它們只能包含 ASCII 字母、數字、連字號和底線。它們有長度限制:每個標籤最多可以有 63 個八位元字節,完整的主機名稱不能超過 255 個八位元組。這些都是 DNS 協定本身的硬性約束,早在國際網域成為概念之前就已經定義了。百分比編碼不適用於主機名,因為產生的字串可能會超出較長單字的標籤限制。
更重要的是,DNS 是一個由全球路由器和伺服器運作的全球系統。並非所有人都理解 UTF-8 或 Unicode。像 %C3%A9 這樣的百分比編碼字符仍然是三個 ASCII 字符,因此它符合 DNS 限制。但這種方法意味著每次查找都必須在傳入時進行百分比編碼並在傳出時進行解碼,從而增加了協定層本身的複雜性。特別是對于主机名,需要更好的解决方案。
IDNA 和 punycode 概述 — xn-- 前缀和引導字符串演算法,定性描述
IDNA(應用程式中的國際化網域名稱規範)透過將非 ASCII 網域編碼為 DNS 可以處理的 ASCII 來解決主機名稱問題。使用的編碼稱為 punycode,這是一種壓縮演演算法,使用前綴 xn-- 後跟引導字串編碼表示形式將 Unicode 文字轉換為 ASCII。演演算法是確定性的:münchen 每次都會變成 xn--mnchen-3ya。任何非 ASCII 主機名稱都必須以這種方式進行轉換,然後才能進行 DNS 解析。
xn-- 前綴向 DNS 和 IDNA 感知軟體發出訊號,表示下列字元是 punycode,而不是原義 ASCII 字母。像 example.xn--mnchen-3ya.com 這樣的網域名稱被 IDNA 感知軟體理解為 example.münchen.com。 Punycode 僅使用 ASCII 字母、數字和連字符,因此它可以毫無問題地完全適合 DNS 標籤。此演演算法將非 ASCII 資訊壓縮為該 ASCII 表示形式。
路徑、查詢和片段保持百分比編碼 — UTF-8 位元組到 %XX,與其他地方一樣
URL 中的其他所有內容(路徑、查詢字串、片段)均使用百分比編碼。非 ASCII 字元首先轉換為 UTF-8 位元組,然後每個位元組寫入為 %HH,其中 HH 是十六進位。路径 /café/ 變為 /caf%C3%A9/. 查询字符串 ?name=josé 變為 ?name=jos%C3%A9。百分比編碼在網路上的任何地方都是標準的:在 HTTP 請求 URL 中、在 HTML 表單中、在 API 中。它不需要 DNS 或路由器進行特殊處理。
百分比編碼也允許安全地表示其他特殊字元。空格變成 %20,斜線(如果必須出現在值內)變成 %2F,依此類推。該方案具有一致性和通用性。它不用於主機名,因為 DNS 不理解 URL 或百分比編碼;它只理解 ASCII 標籤。
工作範例:一個 URL 兼具兩者 — 主機轉換為 punycode,路徑百分比編碼,並排
取得 URL「https://münchen.example/café?city=münchen". 在 DNS 查找之前,主機名稱 münchen 必須轉換為 punycode: https://xn--mnchen-3ya.example/café?city=münchen. 但是等等,路徑和查詢也有非 ASCII。也轉換它們: https://xn--mnchen-3ya.example/caf%C3%A9?city=m%C3%BCnchen. 現在主機名是 puny__ 現在主機名稱是可讀性程式和查詢的原始碼;請求攜帶編碼版本。
在 URL 編碼器和解碼器工具中,貼上包含非 ASCII 文字的路徑,並將單值模式(僅路徑)與全位址模式(完整 URL)進行比較。该工具會向您顯示路径的百分比編碼結果。然而,主機名稱需要單獨的 punycode 轉換;大多數編碼工具不處理內聯,因此請從工具檔案中閱讀。
同形異義字攻擊以及為什麼瀏覽器有時會顯示 punycode — 顯示規則背後的安全推理
惡意行為者可以使用看起來與拉丁字母相同的西里爾字母註冊域名,例如“https://xn--80akhbyknj4f.example"(punycode 中“example.example”的西里爾字母版本)。如果瀏覽器將其解碼為西里爾文字顯示,用戶可能不會注意到其中的差異。為了防止同形異義詞攻擊,瀏覽器有時會顯示 punycode 版本而不是對其進行解碼。出現警告:此網域全部或大部分是非 ASCII,您可能無法辨識這些字元。
URL編解碼器是編碼和解碼的工具,不用於安全評估。如果您使用國際域名,請注意 punycode 表示是網路看到的。
這不包含什麼 - 手動執行 punycode 演演算法或 IDNA 2003 與 2008 差異
IDNA 隨著時間的推移經歷了多個版本:IDNA 2003 和 IDNA 2008 以不同的方式處理某些邊緣情況,特別是在規範化以及規範允許哪些 Unicode 字元方面。一些較舊的系統仍然使用 IDNA 2003,而其他系統已遷移到 IDNA 2008 以實現更好的合規性。如果您正在建立必須跨多個版本相容的系統,那麼這些差異就很重要。始終仔細檢查您的系統需求。
Punycode 使用引導字串壓縮。實作以通用語言存在,但請使用您的主機名稱系統驗證 IDNA 策略。測試解析度和顯示行為而不是假設。
重點:兩個工作的兩種編碼 — URL 編碼器和解碼器如何處理百分比編碼部分,以及為什麼百分比編碼器對於主機名稱來說是錯誤的工具
主機名稱需要 punycode,因為 DNS 是舊協議,只能理解 ASCII 標籤,並有嚴格的長度和字元限制。路徑、查詢和片段使用百分比編碼,因為它在網路上是通用的並沒有這些限制。它們是針對兩個完全不同問題的兩個單獨的解決方案。當遇到非 ASCII URL 時,主機名稱會先進行 punycode 轉換,然後其餘部分使用百分比編碼。
對於大多數開發工作,您的框架或庫會在幕後自動處理此轉換。但是,了解為什麼存在兩種不同的編碼可以防止在調試國際 URL 或成功實現您自己的 URL 處理程式碼時出現混亂。