简体中文

开发者工具 · URL 编码器和解码器

双 URL 编码:%2520 如何发生以及如何检测和撤消它

· 工作原理

url 编码 JavaScript 开发人员工作流程 调试

URL 参数显示 %2520 逐渐解码为 %20,然后解码为空格
原始 ToolAcre 矢量图

%2520 表示空格被编码两次。这篇文章解释了导致它的管道错误、如何识别签名以及多少次解码是安全的。

双 URL 编码:当 %2520 表示空格经过两个编码器

以“my%20file.pdf”而不是“my file.pdf”形式到达的文件名表示双重编码:空格被编码为 %20,然后百分号本身被编码为 %25,在最终 URL 中生成 %2520。系统的每一层(例如客户端代码、Web 框架或反向代理)可能会编码一次。当两个单独的层编码时,单个字符会被破坏。

双编码表面最常出现在复杂的重定向链和模板系统中。开发人员可能会在框架内生成编码的 URL,该框架本身默认对所有输出进行编码。反向代理或内容交付网络可能会对已从后端系统编码到达的 URL 进行重新编码。包含已编码值的参数在嵌套到另一个 URL 结构中之前会再次编码。

为什么 %25 是关键 - 百分号本身被编码,因此 %20 变成 %2520 并且 %C3%A9 变成 %25C3%25A9

双重编码的标志是 %25 ,出现在您通常期望在 URL 或数据中看到单个百分号的位置。在正常编码的 URL 中,除非发送文字“%25”,否则您永远不会看到 %25。如果编码为 %20 的空格再次编码,它将变为 %2520。

像 é 这样的重音字符通常编码为 %C3%A9,当由两个不同的系统按顺序编码两次时,将变为 %25C3%25A9。在数据流经多个服务的生产环境中,学习识别 URL 栏、日志和错误消息中的 %25 模式可以节省无数时间令人沮丧的调试工作。

引入双重编码的地方 - 客户端代码加框架、重定向、代理和模板助手

双重编码会破坏可读性以及服务器端系统正确解析 URL 的能力。正确编码后,名为“my file.pdf”的文件将变为“my%20file.pdf”。如果编码的字符串被重新编码(可能通过某种形式),它将变成“my%2520file.pdf”。

当服务器收到此消息并对其进行解码一次时,它将“my%20file.pdf”视为文字文件名,而不是将其识别为“my file.pdf”。任何期望仅接收单个解码通道的应用程序都将收到损坏的结果。更糟糕的是,开发人员解码两次以修复仅编码一次的值的问题实际上会通过额外的解码过程破坏合法数据。

工作示例:一次解码一个双重编码的 URL — 每一次显示什么以及何时停止

客户端 JavaScript 代码和服务器端框架默认值是生产系统中意外双重编码的最常见来源。 JavaScript 应用程序可能对某个值使用encodeURIComponent,然后将其直接传递到默认情况下对所有字符串输出进行编码的框架,从而对百分号进行第二次编码。用于清理 URL 的反向代理层可能会对已从后端应用程序预编码的参数进行重新编码。

通过连接用户提供的输入与框架帮助函数构建的重定向 URL 可以同时在两个步骤中进行编码。工作示例:用户通过 HTML 表单提交“test&value”,浏览器将其编码为“test%26value”。该框架看到文字百分比文本并对其进行编码,生成“test%2526value”。一次解码给出“test%26value”,仍然是错误的。

当故意使用双重编码时 — 一个 URL 包含在另一个 URL 的查询参数中

故意双重编码在一种特定情况下是有效的:当一个 URL 必须在另一个 URL 的查询参数内传输时。 OAuth 流和登录返回链接有时需要将一个完整的 URL 嵌套在另一个 URL 中。内部 URL 必须首先进行完全百分比编码,然后必须将整个编码字符串再次编码为外部 URL 的参数值。

这种双重编码是经过深思熟虑的,并且在这些情况下是绝对必要的。外部参数解析器解码一次,生成仍然编码的内部 URL。然后内部系统再次解码,恢复原始 URL。关键是理解意图并在代码注释中清楚地记录下来,以供未来的维护人员使用。

常见错误 - 解码直到没有任何变化,这会破坏合法包含 %25 的值

典型且危险的错误是重复解码,直到没有任何变化,这将破坏实际数据中合法包含百分号的值。像“discount%2525”这样的参数(表示编码为参数值的文字“%25”,然后再次编码以进行传输)在设计上是完全正确的。解码一次会产生“discount%25”,这仍然是正确的。第二次解码会得到“discount%”,这是错误的并且会丢失信息。

开发人员可能会认为“%25”是一个错误并重复解码,丢失百分号。相反,解码次数与您的架构要求的次数完全相同:一次用于参数,两次嵌套。计算层数以了解正确的解码操作。

这不包括什么 - HTML 实体编码分层在 URL 之上,由 HTML 实体转义器处理

常见错误包括使用encodeURIComponent 对整个 URL 进行编码,然后期望斜杠和冒号充当结构分隔符,但在编码后却不能这样做。另一个常见错误是混合不同的编码标准:某些代码使用 RFC 3986 的百分比编码,而其他代码使用带有代表空格的加号的形式编码。像“my+file”这样的值确实变得不明确——它可能意味着“我的文件”,也可能意味着带有加号的文字文本“my+file”。

如果百分比编码首先触及“my+file”,则它变为“my%2Bfile”。如果接下来进行形式解码,期望加号作为空格,那么它仍然是错误的。跨层的一致性至关重要。每个系统必须使用相同的编码标准,或者必须明确记录每一层。

要点:每层只编码一次 — URL 编码器和解码器如何让您一次解码一次并查看每个中间结果

一旦成功识别生产系统中发生的双重编码,修复完全取决于管道中重复发生的位置。如果客户端代码和框架都在编码,请从其中之一完全删除编码。如果参数经过多个后端服务,请跟踪每个服务的完整路径,并找到哪个服务在不应该编码的情况下进行编码。

通过将示例数据传递到完整的端到端管道来彻底测试修复,并验证数据完全未更改地到达目的地。清楚地记录每个边界处的编码假设:“此端点返回百分比编码的参数”或“此中间件需要原始 UTF-8 并对其应用编码”。包括未来开发人员在该文档中预期的解码次数。