开发者工具 · 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. 现在主机名是 punycode,路径和查询是百分比编码的。浏览器显示原始 Unicode 版本以提高可读性;HTTP 请求携带编码版本。
在 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 处理代码时出现混乱。