冒号 `:` 是否适合用于友好的URL?

136
我们正在设计一个URL系统,将应用程序部分指定为由斜杠分隔的单词。具体来说,在GWT中,相关部分的URL将在哈希中(将由客户端的控制器层解释)。
http://site/gwturl#section1/section2

有些部分可能需要额外的属性,我们希望使用:指定这些属性,以便URL的部分是明确无歧义的。代码将首先在/上拆分,然后在:上进行拆分,就像这样:

http://site/gwturl#user:45/comments
当然,我们这样做是为了使URL友好。因此,我们希望确保浏览器或任何其他系统不会对具有特殊含义的这些字符进行URL编码,并以以下方式结束URL:
http://site/gwturl#user%3A45/comments <--- BAD

在这种方式下使用冒号安全(我的意思是不会自动编码)对于浏览器、书签系统,甚至JavaScript或Java代码而言,是否安全?


也许更清楚地指明你只在客户端使用URL是个好主意,因为很多答案(包括我的)似乎都假设你会使用HTTP将URL发送到服务器。 - Veger
编辑以添加澄清,片段的使用发生在客户端。 - Nicole
我很好奇:10个月后,这个URL方案对你有用吗?我正在考虑使用相同的方案。 - Jonathan Swinney
1
@Jonathan Swinney,很遗憾我已经从这个项目(和公司)离开了,尽管这里的答案让我满意,并且证明了这是正确的方法。如果我要开始一个新的项目,我会使用这个方案,但我也会确保使用 #! 来表示页面是有状态的 - 参见 http://googlewebmastercentral.blogspot.com/2009/10/proposal-for-making-ajax-crawlable.html (这个提议已经被像Facebook这样的重度 AJAX 用户所遵循)。 - Nicole
2
我刚刚发现WhatsApp会在第一个冒号处截断URL,例如它会使谷歌地图的URL无效。因此,对它进行转义是很重要的。 - Petruza
为什么要在URL中使用冒号?以下的URL是否有效?"../video/:videoId" - vikramvi
11个回答

101

我最近编写了一个URL编码器,所以这个还很清晰。

http://site/gwturl#user:45/comments

片段部分user:45/comments)中的所有字符都是符合RFC 3986 URI规范的。

ABNF的相关部分:

fragment      = *( pchar / "/" / "?" )
pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
pct-encoded   = "%" HEXDIG HEXDIG
sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
                 / "*" / "+" / "," / ";" / "="

除了这些限制之外,片段部分没有定义的结构,只有你的应用程序给出的结构。方案http仅表示您不将此部分发送到服务器。

编辑:

天啊!

尽管我对URI规范有所坚称,不可靠的人提供了正确的答案,当他指出HTML 4规范限制元素名称/标识符

请注意,标识符规则在HTML 5中正在改变。URI限制仍将适用(在撰写本文时,HTML 5使用URI存在一些未解决的问题)。


我认为你有所发现,你能再解释一下吗?不向服务器发送这个并不是问题,因为我们正在使用 GWT。我只是不确定你引用的那个部分指定的语法是否清楚。 - Nicole
但是 : 是一个通用分隔符,而不是子分隔符。 - bobince
1
分号在pchar中是合法的,因此它是否在sub-delim或gen-delim中并不重要。 - Veger
@Renesis - 我已经忘记了HTML 4的限制 - 请查看此答案:https://dev59.com/bXI-5IYBdhLWcg3wBjdR#2053640 - McDowell
这是一个非常好的答案。我已经点赞了,但我还想过来告诉你我喜欢它的所有方面。 - Joshua Cheek
显示剩余3条评论

96

61
最相关的答案。我们都知道在网站开发中,规格说明书中写的与实际情况关系不大。你不会得到比“全球前十的网站之一这样做”更好的“安全性”保证。 - Steven Collins
5
比起之前回答这个问题的三年前,给出的答案几乎没有什么区别,没有更多相关性。 :) - Martin James

69
除了麦克道尔对URI标准的分析外,还要记住片段必须是有效的HTML锚点名称。根据http://www.w3.org/TR/html4/types.html#type-name,ID和NAME令牌必须以字母([A-Za-z])开头,后面可以跟任意数量的字母、数字([0-9])、连字符(“-”)、下划线(“_”)、冒号(“:”)和句点(“.”)。
所以你很幸运,“:”是被明确允许的。而且没有人应该用“%”转义它,不仅因为“%”在那里是非法字符,而且因为片段必须与锚点名称一一匹配,因此没有代理应该尝试以任何方式篡改它们。
但是你需要进行测试。Web标准并没有严格遵循,有时标准会发生冲突。例如HTTP/1.1 RFC 2616不允许在请求URL中使用查询字符串,而HTML在使用GET方法提交表单时构造了一个查询字符串。最终实现在现实世界中胜出。

10

我不会指望这种情况发生。它很可能被许多用户代理编码为%3A


1
@arbales:是的。一些不太合规的用户代理会将不合规的URL保持原样。 - Asaph

5

来自 URLEncoder javadoc:

有关 HTML 表单编码的更多信息,请参阅 HTML 规范

在对字符串进行编码时,遵循以下规则:

  • 字母数字字符 "a" 到 "z"、"A" 到 "Z" 和 "0" 到 "9" 保持不变。
  • 特殊字符 ".", "-", "*" 和 "_" 保持不变。
  • 空格字符 " " 转换为加号 "+"。
  • 所有其他字符都是不安全的,并首先使用某种编码方案将其转换为一个或多个字节。然后,每个字节都用三个字符的字符串 "%xy" 来表示,其中 xy 是字节的两位十六进制表示。建议使用的编码方案是 UTF-8。但是,出于兼容性原因,如果未指定编码,则使用平台的默认编码。

也就是说,: 是不安全的。


5

谷歌也使用冒号。

这个规范中,他们使用冒号命名自定义方法。


4

我没有看到Firefox或IE8对包含该字符的部分维基百科URLs进行编码。


1
Opera也保留了分号,但依赖这种行为并不是一个好习惯。 - Veger
1
Renesis 正在谈论 URL 片段而不是 URL 路径。 - Gumbo
在撰写这个问题时,维基百科是我想到的一个地方。那么它使用冒号是否在技术上无效/不安全呢?我经常看到维基百科URL中编码了 ( 和 ) ,但从未看到过冒号,这让我有点困惑。 - Nicole
3
网络档案馆(Wayback Machine)的许多链接中都含有冒号,例如:http://web.archive.org/web/20080822150704/http://stackoverflow.com/。 - barrowc

2

如果协议需要认证,冒号将用作用户名和密码之间的分隔符。


0

Apache的URIBuilder和JAX-RS的UriBuilder类在处理:时有所不同(它们也会对花括号进行不同的处理)

new URIBuilder("http://localhost").setCustomQuery("foo=a:b&bar={}").buildString()

输出

http://localhost?foo=a:b&bar=%7B%7D

UriBuilder.fromPath("http://localhost").queryParam("foo", "a:b").queryParam("bar", "{}").toTemplate()

输出

http://localhost?foo=a%3Ab&bar={}

所以Apache URIBuilder似乎不会对:进行编码,但它会对{}进行编码,而对于JAX-RS UriBuilder则相反。


0

那个页面没有说明为什么它们不安全。参考的RFC2396也没有说应该转义它。此外,提供的转换脚本也没有对其进行编码(至少在Chrome 9中)。 - Adam Lindberg
Adam,你是错误的。它直接说明了什么和为什么。 - ktamlyn
从文章中可以看出,为什么冒号应该被转义。这似乎是一个风格的争论。
URL使用一些字符来特殊定义其语法。当这些字符在URL内没有用于其特殊角色时,它们需要被编码。
- grmdgs

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接