RFC3986 - 哪些 pchars 需要进行百分比编码?

Posted

技术标签:

【中文标题】RFC3986 - 哪些 pchars 需要进行百分比编码?【英文标题】:RFC3986 - which pchars need to be percent-encoded? 【发布时间】:2011-08-20 07:04:45 【问题描述】:

我需要为 URI 生成一个 href。除了需要百分比编码的保留字符之外,一切都很容易,例如指向/some/path;element 的链接应显示为<a href="/some/path%3Belement">(我知道path;element 表示单个实体)。

最初我正在寻找一个 Java 库来执行此操作,但最终我自己编写了一些东西(请查看下面的 Java 失败之处,因为这个问题不是 Java 特定的)。 p>

所以,RFC 3986 does suggest when NOT to encode。当我读到它时,当字符属于unreserved (ALPHA / DIGIT / "-" / "." / "_" / "~") 类时,这应该发生。到目前为止,一切都很好。但是相反的情况呢? RFC 只提到百分比 (%) 总是需要编码。但是其他人呢?

问题:假设所有不是未保留的内容都可以/应该进行百分比编码是否正确?例如,左括号( 不一定需要编码,但分号; 需要。如果我不对其进行编码,我最终会在关注<a href="/first;second"> 时寻找/first*。但是按照<a href="/first(second">,我总是最终寻找/first(second,正如预期的那样。让我感到困惑的是,就 RFC 而言,(; 都在同一个 sub-delims 类中。正如我想象的那样,对所有非保留的内容进行编码是一个安全的选择,但是在本地化 URI 方面,SEO 能力和用户友好性又如何呢?

现在,Java 库失败了。我试过这样做new java.net.URI("http", "site", "/pa;th", null).toASCIISTring() 但这给了http://site/pa;th,这是不好的。观察到类似的结果:

javax.ws.rs.core.UriBuilder Spring's UriUtils - 我已经尝试过 encodePath(String, String)encodePathSegment(String, String)

[*] /first 是点击<a href="/first;second"> 时在服务器端调用HttpServletRequest.getServletPath() 的结果

编辑:我可能需要提一下,在 Tomcat 下观察到了这种行为,并且我检查了 Tomcat 6 和 7 的行为方式相同。

【问题讨论】:

【参考方案1】:

假设不是未保留的所有内容都可以/应该进行百分比编码是否正确?

没有。 RFC 3986 是这样说的:

"在正常情况下,URI 中的八位字节被百分比编码的唯一时间是在从其组成部分生成 URI 的过程中。这是当实现确定要保留哪些字符时用作子组件分隔符,可以安全地用作数据。"

这意味着您决定需要对哪些分隔符(即<delimiter> 字符)进行编码取决于上下文。不需要编码的就不编码了。

例如,如果 / 出现在路径组件中,则不应对其进行百分比编码,但应在查询或片段中出现时对其进行百分比编码。

所以,事实上,; 字符(它是 <reserved> 的成员不应该自动进行百分比编码。事实上,java URL 和 URI 类不会这样做;具体参见 URI(...) javadoc第 7 步)了解如何处理 <path> 组件。

本段加强了这一点:

“保留字符的目的是提供一组分隔字符,这些字符可与 URI 中的其他数据区分开来。在用相应的百分比编码八位字节替换保留字符方面不同的 URI 是不等价的. 对保留字符进行百分比编码,或对与保留字符相对应的百分比编码八位字节进行解码,将改变大多数应用程序解释 URI 的方式。因此,保留集中的字符受到规范化保护,因此可以安全使用由特定于方案和特定于生产者的算法用于分隔 URI 中的数据子组件。"

也就是说,包含百分比编码; 的 URL 与包含原始 ; 的 URL 不同。最后一句话暗示它们不应该被自动编码或解码。


这给我们留下了一个问题 - 为什么您想要 ; 被百分比编码?

假设您有一个 CMS,人们可以在其中创建具有任意路径的任意页面。稍后,我需要生成指向所有页面的 href 链接,例如站点地图组件。因此,我需要一种算法来知道要转义哪些字符。在这种情况下,分号必须按字面意思处理,并且应该转义。

对不起,分号不应该被转义。

就 URL / URI 规范而言,; 没有特殊含义。它可能对特定的网络服务器/网站具有特殊意义,但一般而言(即没有特定的网站知识)您无法知道这一点。

如果 ; 在特定 URI 中确实具有特殊含义,那么如果您对它进行百分比转义,那么您就破坏了该含义。例如,如果站点使用; 允许将会话令牌附加到路径中,那么百分比编码将阻止它识别会话令牌...

如果; 只是某个客户端提供的数据字符,那么如果对它进行百分比编码,则可能会改变 URI 的含义。这是否重要取决于服务器的工作;即是否解码作为应用程序逻辑的一部分。

这意味着要知道“正确的做法”需要深入了解 URI 对最终用户和/或网站的意义。这需要先进的读心技术来实施。我的建议是让 CMS 通过适当地转义 URI 路径 将它们传递给您的软件的任何分隔符来解决它。该算法必然将特定于 CMS 和内容交付平台。它/他们将响应对由 URL 标识的文档的请求,并且需要知道如何解释它们。

(支持任意人使用任意路径有点疯狂。必须有 一些 限制。例如,甚至 Windows 都不允许您在文件名组件中使用文件分隔符。所以你是必须在某个地方有一些界限。这只是决定它们应该在哪里的问题。)

【讨论】:

假设您有一个 CMS,人们可以在其中创建具有任意路径的任意页面。稍后,我需要生成指向所有页面的 href 链接,例如站点地图组件。因此,我需要一种算法来知道要转义哪些字符。在这种情况下,分号必须按字面意思处理,并且应该转义。【参考方案2】:

ABNF 表示绝对路径部分:

 path-absolute = "/" [ segment-nz *( "/" segment ) ]
 segment       = *pchar
 segment-nz    = 1*pchar
 pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
 pct-encoded   = "%" HEXDIG HEXDIG
 unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
 reserved      = gen-delims / sub-delims
 sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
               / "*" / "+" / "," / ";" / "="

pchar 包含子分隔符,因此您不必在路径部分中对任何这些进行编码::@-._~!$&'()*+,;=

我写了 my own URL builder,其中包括一个路径编码器 - 一如既往,警告购买者。

【讨论】:

嗯,你是说; 不需要百分比编码吗?一开始我也是这么想的,但似乎是错误的(见我帖子的最后一章)。或者这可能是一个 Tomcat 错误? @mindas - 我猜 Tomcat 正在遵循旧的 URI 规范 - RFC 2396。在本规范中,;= 保留用于路径段中的参数。一些应用服务器仍然通过 URL 重写 (/foo/bar;jsessionid=***) 实现这些会话跟踪。如果您决定下载我的库,请参阅示例下的EditPathParams.java

以上是关于RFC3986 - 哪些 pchars 需要进行百分比编码?的主要内容,如果未能解决你的问题,请参考以下文章

如何对 RFC 3986 字符串进行转义

Java 和 RFC 3986 URI 编码

为啥 %(百分比)在 RFC 3986(URI 语法)中不被视为保留字符?

什么样的 URL 不符合 RFC 3986 但符合 RFC 1808、RFC 1738 和 RFC 2732?

RFC 3986 中的 HTTP 请求主机值语法

根据 RFC 3986 的无效 URI 示例