哪些字符会使网址无效?


514

哪些字符会使网址无效?

这些有效的网址吗?

  • example.com/file[/].html
  • http://example.com/file[/].html

42
验证时,您应始终“积极思考”:询问“什么是有效的”,其他所有内容均无效。比起所有可能的无效字符,对(很少)有效字符进行测试要安全得多(也更容易!)。
mfx

Answers:


600

通常,由RFC 3986(请参见第2节:字符)定义的URI 可以包含以下84个字符中的任何一个:

ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~:/?#[]@!$&'()*+,;=

请注意,此列表并未说明这些字符在URI中可能出现的位置。

任何其他字符都需要使用百分比编码(%hh)进行编码。URI的每个部分对于需要用百分比编码的单词表示哪些字符都有进一步的限制。


31
(当然,字符列表并没有说明它们在uri中可能出现的位置)
Eamon Nerbonne

75
这是一个正则表达式,它将确定整个字符串是否仅包含上面的字符:/ ^ [!#$&-; =?-[] _ a-
z〜

43
@techiferous,是的,我忘记了允许“%”转义字符。它看起来应该更像: /^([!#$&-;=?-[]_a-z~]|%[0-9a-fA-F]{2})+$/ 您是否发现它应该接受其他任何内容?(请注意,正则表达式仅检查字符串是否包含有效的URL字符,而不检查字符串是否包含格式正确的URL。)
Leif Wickland

12
@Timwi RFC 3986说:“百分比编码的八位位组被编码为字符三元组,由百分比字符“%”和代表该八位位组数值的两个十六进制数字组成。” 它还说:“因为百分比(“%”)字符用作百分比编码的八位位组的指示符,所以它必须被百分比编码为“%25”才能将该八位位组用作URI中的数据。” 我读这句话是说,“%”只有在其后跟两个十六进制数字时才会出现。你怎么看?
Leif Wickland'1

13
@Weeble我的正则表达式通过使用范围包括了这些字符。之间 ';' 在“?”之间 和'[',您会找到所有您没有看到的字符。
Leif Wickland

193

为了进一步说明问题并直接解决上述问题,有几类字符会引起URL和URI问题。

有些字符是不允许使用的,并且永远不应出现在URL / URI中,保留字符(如下所述)和某些在某些情况下可能会引起问题的字符,但它们被标记为“不明智”或“不安全”。为何限制字符的解释在RFC-1738(URL)和RFC-2396(URI)中明确说明。请注意,较新的RFC-3986(对RFC-1738的更新)定义了在给定上下文中允许使用哪些字符的结构,但是较旧的规范对以下规则不允许使用哪些字符进行了更简单,更通用的描述。

URI语法中不允许使用的排除的US-ASCII字符:

   control     = <US-ASCII coded characters 00-1F and 7F hexadecimal>
   space       = <US-ASCII coded character 20 hexadecimal>
   delims      = "<" | ">" | "#" | "%" | <">

排除字符“#”是因为它用于从片段标识符中分隔URI。排除百分比字符“%”,因为它用于转义字符的编码。换句话说,“#”和“%”是必须在特定上下文中使用的保留字符。

允许使用不明智的字符列表,但可能会导致问题:

   unwise      = "{" | "}" | "|" | "\" | "^" | "[" | "]" | "`"

在查询组件中保留的字符和/或在URI / URL中具有特殊含义的字符:

  reserved    = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | ","

上面的“保留”语法类指的是URI中允许的那些字符,但是通用URI语法的特定组件中可能不允许的那些字符。并非在所有上下文中都保留“保留”集中的字符。例如,主机名可以包含可选的用户名,因此它可能类似于ftp://user@hostname/“ @”字符具有特殊含义的地方。

这是一个URL的示例,该URL包含无效和不明智的字符(例如'$','[',']'),并且应正确编码:

http://mw1.google.com/mw-earth-vectordb/kml-samples/gp/seattle/gigapxl/$[level]/r$[y]_c$[x].jpg

URI / URL的某些字符限制取决于编程语言。例如,“ |” (0x7C)字符(尽管在URI规范中仅被标记为“不明智”)会在Java java.net.URI构造函数中引发URISyntaxException,因此,不允许使用类似URL,并且必须对其进行编码,就像将Java与URI对象实例一起使用一样。http://api.google.com/q?exp=a|bhttp://api.google.com/q?exp=a%7Cb


2
优秀,详尽的答案,是直接回答实际问题的唯一答案。保留部分可能需要的工作,如文字?只是罚款收到查询部分,但是不可能的,而且我不认为@属于在任何邮件列表。哦,不是%25最后一个字符串,不是%7C吗?
鲍勃·斯坦因

1
谢谢。不错:在示例中,%25是一个错字。直接在RFC-2396中为“保留”语法说明添加了脚注。
JasonM1 2013年

1
这个答案还不错,但是有些困惑和错误。最初,您混用了不允许的字符和保留的字符(完全不同的东西),在“不明智的”字符和其他不允许的字符(在RFC 3986中删除,甚至在RFC 2396中在语法上不相关)之间进行了太多区分,并且令人困惑地显示了所有保留字符,作为“在查询组件内”保留的列表。
Mark Amery

1
谢谢,不是要把不允许的和保留的分组为同一组。更新了答案。RFC-2396中的IMHO规则虽然比3986中的更新规则更旧,但更易于理解。答案反映了更多有关一般而言哪些字符可能比较麻烦的问题,而不是确切地允许或不允许的上下文。
JasonM1 2016年

1
值得注意的是,在最近的版本(7.0.73 +,8.0.39 +,8.5.7 +)中,Tomcat已经开始拒绝具有HTTP 400错误的“不明智”类别字符的请求:“在请求目标中发现无效字符。有效字符在RFC 7230和RFC 3986中定义”
Philip

100

这里大多数现有的答案都是不切实际的,因为它们完全忽略了地址的实际用法,例如:

首先,离题。什么这些地址?它们是有效的网址吗?

从历史上看,答案是“否”。根据RFC 3986,从2005年开始,此类地址不是URI(因此不是URL,因为URL 是URI的一种)。按照2005 IETF标准的术语,我们应该适当地将它们称为RFC 3987中定义的IRI(国际化资源标识符),从技术上讲,它不是URI,但只需对IRI中的所有非ASCII字符进行百分比编码,就可以将其转换为URI。 。

根据现代规范,答案是“是”。在WHATWG生活标准只是分类的一切,这在以前被称为“URI的”或“IRIS”的“网址”。这使专门术语与未阅读该规范的普通人如何使用“ URL”一词保持一致,这是该规范的目标之一

WHATWG生活标准允许使用哪些字符?

按照“ URL”这个较新的含义,允许使用什么字符?在网址的许多部分(例如查询字符串和路径),我们允许使用任意的“ URL单位”,即

URL代码点百分比编码的byte

什么是“ URL代码点”?

URL代码点是ASCII字母,U + 0021(!),U + 0024($),U + 0026(),U + 0027('),U + 0028左括号,U + 0029右括号,U + 002A(*),U + 002B(+),U + 002C(,),U + 002D(-),U + 002E(。),U + 002F(/),U + 003A(:),U + 003B (;),U + 003D(=),U + 003F(?),U + 0040(@),U + 005F(_),U + 007E(〜)和范围在U + 00A0到U之间的代码点+ 10FFFD(含),不包括替代字符和非字符。

(请注意,“ URL代码点”列表中不包含%,但%如果它们是百分比编码序列的一部分,则可以在“ URL代码单元”中使用。)

我可以发现的唯一规范允许使用该集中包含的字符的地方是host,其中IPv6地址包含在[]字符中。URL中的其他任何地方,都允许使用URL单元,或者使用一些甚至更具限制性的字符集。

旧RFC允许使用哪些字符?

出于历史原因,并且由于此处未在其他答案中对其进行全面探讨,因此让我们研究一下旧规范对是否允许使用。

首先,我们有两种类型的RFC 3986 保留字符

  • :/?#[]@,它们是RFC 3986中定义的URI通用语法的一部分
  • !$&'()*+,;=,它不是RFC通用语法的一部分,但保留用作特定URI方案的语法组件。例如,分号和逗号被用作的语法的一部分的数据的URI,并&=被用作普遍存在的一部分?foo=bar&qux=baz在查询字符串格式(其由RFC 3986规定)。

上面的任何保留字符都可以合法地在URI中使用,而无需进行编码,以达到其语法目的,或者在某些地方不能将其用作数据中的文字字符,而这些地方不能被误解为具有其语法目的的字符。(例如,尽管/URL中具有语法含义,但是您可以在查询字符串中使用未编码的语法,因为它在查询字符串中没有含义。)

RFC 3986还指定了一些未保留的字符,这些字符始终可以简单地用于表示数据而无需任何编码:

  • abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~

最后,%允许字符本身进行百分比编码。

这样只剩下以下禁止在URL中出现的ASCII字符:

  • 控制字符(字符0-1F和7F),包括换行符,制表符和回车符。
  • "<>\^`{|}

ASCII中的所有其他字符都可以合法地出现在URL中。

然后,RFC 3987使用以下unicode字符范围扩展了该保留字符集:

  %xA0-D7FF / %xF900-FDCF / %xFDF0-FFEF
/ %x10000-1FFFD / %x20000-2FFFD / %x30000-3FFFD
/ %x40000-4FFFD / %x50000-5FFFD / %x60000-6FFFD
/ %x70000-7FFFD / %x80000-8FFFD / %x90000-9FFFD
/ %xA0000-AFFFD / %xB0000-BFFFD / %xC0000-CFFFD
/ %xD0000-DFFFD / %xE1000-EFFFD

考虑到最新的Unicode 块定义,从旧规范中选择这些块似乎很奇怪和任意;这可能是因为自编写RFC 3987以来的十年中已添加了这些块。


最后,也许值得一提的是,仅仅知道哪些字符可以合法出现在URL中还不足以识别某些给定的字符串是否为合法URL,因为某些字符仅在URL的特定部分才是合法的。例如,保留字符[]是合法的,可以作为URL中IPv6文字主机的一部分,例如http:// [1080 :: 8:800:200C:417A] / foo,但在任何其他上下文中都不合法,因此OP的示例http://example.com/file[/].html是非法的。


3
详尽参考的加号(例如RFC)
Yan Foto

19

在补充问题中,您询问是否www.example.com/file[/].html有效的URL。

该URL无效,因为URL是URI的一种,并且有效URI必须具有类似的方案http:(请参阅RFC 3986)。

如果您想询问是否http://www.example.com/file[/].html有效的URL,那么答案仍然是否,因为方括号字符在那里无效。

方括号字符为URL保留以下格式:(http://[2001:db8:85a3::8a2e:370:7334]/foo/bar即IPv6文字而不是主机名)

如果您想完全理解此问题,则值得仔细阅读RFC 3986。


阅读完RFC之后,我更倾向于同意@Stephen C进行更详细的解释。
skolima 2011年

URL不是URI的子集。在[]不在URI有效期为我所看到的几乎解析器。这实际上已经拧我在现实世界中:stackoverflow.com/questions/11038967/...
亚当根特

@AdamGent URL非常是URI的子集。它们之间的唯一区别是它们是否描述资源的位置-这是语义上的区别,而不是句法上的区别。如果您看到的将自己标记为“ URI”解析器的解析器将方括号与将其标记为“ URL”解析器的解析器不同,那么这纯属巧合,并非由URL和URI之间的任何差异引起的。
Mark Amery

@Mark Amery类似于说C ++是C的超集。在大多数情况下,但并非完全如此,因为(URL和C)年龄大得多,因此它们必须包含不太严格的行为。问题是URL解析器将解析无效的URI ...而且我的意思是大多数(坦白地说,我厌倦了跨多种语言指出这一点)这并不是巧合,因为它具有向后兼容性。我们是否可以同意网址规范至少是较旧的?
亚当·根特

@MarkAmery来自Python,C#,Java和某些C库的解析器将Unwise非常重视URI,但可以使用URL库。那是没有可忽略的标志Unwise。我将不得不检查URL的Rust lang(因为它是为浏览器构建的,我很好奇它的作用)。不过,大多数浏览器也会愉快地传递“ [”,“]”。因此,从理论上讲,就像我对C / C ++所说的那样,它们是sub / super,但现实并非如此。它高度依赖于super / subset的规范和语义的解释。
亚当·根特

12

RFC 3986中定义了可以在URI(URLURI的一种类型)中使用的所有有效字符。

如果其他所有字符都先经过“ URL编码”,则可以在URL中使用。这涉及更改特定“代码”的无效字符(通常以百分比符号(%)的形式,后跟十六进制数字)。

HTML URL编码参考此链接包含无效字符的编码列表。


对于Unicode字符,Wikipedia文章Percent-encoding表示:“通用URI语法规定,提供URI中字符数据表示的新URI方案实际上必须表示未经保留的字符集,而无需进行翻译,并应根据UTF-8将所有其他字符转换为字节,然后对这些值进行百分比编码。”
DavidRR

9

几个Unicode字符范围是有效的HTML5,尽管使用它们可能仍然不是一个好主意。

例如,href文档说http://www.w3.org/TR/html5/links.html#attr-hyperlink-href

和元素上的href属性必须具有一个值,该值是一个有效的URL,可能被空格包围。

然后,“有效URL”的定义指向http://url.spec.whatwg.org/,其目的是:

使RFC 3986和RFC 3987与现代实现保持一致,并在此过程中将其淘汰。

该文档将URL代码点定义为:

ASCII字母数字,“!”,“ $”,“&”,“'”,“(”,“)”,“ *”,“ +”,“,”,“-”,“。”,“ /” ,“:”,“;”,“ =”,“?”,“ @”,“ _”,“〜”和代码点,范围为U + 00A0至U + D7FF,U + E000至U + FDCF ,U + FDF​​0至U + FFFD,U + 10000至U + 1FFFD,U + 20000至U + 2FFFD,U + 30000至U + 3FFFD,U + 40000至U + 4FFFD,U + 50000至U + 5FFFD,U +60000至U + 6FFFD,U + 70000至U + 7FFFD,U + 80000至U + 8FFFD,U + 90000至U + 9FFFD,U + A0000至U + AFFFD,U + B0000至U + BFFFD,U + C0000到U + CFFFD,U + D0000到U + DFFFD,U + E1000到U + EFFFD,U + F0000到U + FFFFD,U + 100000到U + 10FFFD。

语句中使用了术语“ URL代码点”:

如果c不是URL代码点,也不是“%”,则分析错误。

在解析算法的几个部分中,包括架构,权限,相对路径,查询和片段状态:因此基本上是整个URL。

另外,验证器http://validator.w3.org/传递URL,如"你好",而不传递URL字符,如空格"a b"

当然,正如Stephen C提到的,它不仅与字符有关,而且与上下文有关:您必须了解整个算法。但是,由于在算法的关键点上使用了“ URL代码点”类,因此可以很好地了解您可以使用或不使用什么。

另请参阅:URL中的Unicode字符


5

我需要选择字符以将URL拆分为字符串,因此我决定创建一个自己无法在URL中找到的字符列表:

>>> allowed = "-_.~!*'();:@&=+$,/?%#[]?@ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
>>> from string import printable
>>> ''.join(set(printable).difference(set(allowed)))
'`" <\x0b\n\r\x0c\\\t{^}|>'

因此,可能的选择是换行符,制表符,空格,反斜杠和"<>{}^|。我想我会选择空格或换行符。:)


2

并不是真正回答您的问题,但验证url确实是一个严重的问题。您最好验证域名,并将查询的一部分留在url中。那是我的经验。您还可以通过ping通URL并查看它是否导致有效的响应,但是对于这样一个简单的任务而言,可能太多了。

用于检测网址的正则表达式非常丰富,请在Google上添加:)



此答案建议URL验证不是正则表达式的工作,而是语言/平台特定的库的工作
DavidRR 2014年

0

我正在实现旧的http(0.9、1.0、1.1)请求和响应读取器/写入器。请求URI是最有问题的地方。

您不能照原样使用RFC 1738、2396或3986。许多旧的HTTP客户端和服务器都允许使用更多字符。因此,我基于意外发布的Web服务器访问日志进行了研究:"GET URI HTTP/1.0" 200

我发现URI中经常使用以下非标准字符:

\ { } < > | ` ^ "

这些字符在RFC 1738中描述为不安全

如果要与所有旧的HTTP客户端和服务器兼容-必须在请求URI中允许这些字符

请在http-og中阅读有关此研究的更多信息。


-4

我想出了一些PHP的正则表达式,可以将文本中的URL转换为锚标记。(首先,它将所有www。网址转换为http://,然后将所有带有https?://的网址转换为href = ... html链接

$string = preg_replace('/(https?:\/\/)([!#$&-;=?\-\[\]_a-z~%]+)/sim', '<a href="$1$2">$2</a>', preg_replace('/(\s)((www\.)([!#$&-;=?\-\[\]_a-z~%]+))/sim', '$1http://$2', $string) );


4
-1; 除了它们都以某种方式包含URL外,这与所提出的问题无关。
Mark Amery
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.