ChatGPT解决这个技术问题 Extra ChatGPT

哪些字符使 URL 无效?

哪些字符使 URL 无效?

这些是有效的 URL 吗?

example.com/file[/].html

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

验证时,您应该始终“积极思考”:询问“什么是有效的”,其他一切都是无效的。对(少数)有效字符进行测试比所有可能的无效字符更安全(也更容易!)。

C
Community

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

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

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

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


(当然,字符列表并没有说明它们可能出现在 uri 中的哪个位置)
这是一个正则表达式,它将确定整个字符串是否仅包含上述字符: /^[!#$&-;=?-[]_a-z~]+$/
@techiferous,是的,我忘了允许“%”转义字符。它应该看起来更像:/^([!#$&-;=?-[]_a-z~]|%[0-9a-fA-F]{2})+$/ 您发现它应该接受的还有其他内容吗? (需要明确的是,该正则表达式仅检查字符串是否包含有效的 URL 字符,而不是字符串是否包含格式正确的 URL。)
@Timwi RFC 3986 说,“百分比编码的八位字节被编码为一个字符三元组,由百分比字符“%”后跟代表该八位字节数值的两个十六进制数字组成。”它还说,“因为百分号 (“%”) 字符用作百分比编码八位字节的指示符,所以它必须以百分比编码为“%25”,才能将该八位字节用作 URI 中的数据。”我读到它是说“%”只有在后面跟着两个十六进制数字时才会出现。你怎么读?
@Weeble 我的正则表达式通过使用范围来包含这些字符。之间 ';'在“?”之间和 '[' 你会发现所有你没有看到的字符。
C
CodeMonkey

此示例中的 '[' 和 ']' 是“不明智的”字符,但仍然合法。如果 [] 中的“/”是文件名的一部分,那么它是无效的,因为“/”是保留的,应该正确编码:

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

为了添加一些说明并直接解决上述问题,有几类字符会导致 URL 和 URI 出现问题。

有一些字符是不允许出现的并且永远不应出现在 URL/URI 中、保留字符(如下所述)以及在某些情况下可能会导致问题但被标记为“不明智”或“不安全”的其他字符。 RFC-1738 (URLs) 和 RFC-2396 (URIs) 中清楚地说明了字符受限的原因。请注意,较新的 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      = "{" | "}" | "|" | "\" | "^" | "[" | "]" | "`"

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

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

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

下面是一个 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,因此像 http://api.google.com/q?exp=a|b 这样的 URL不允许,如果将 Java 与 URI 对象实例一起使用,则必须将其编码为 http://api.google.com/q?exp=a%7Cb


优秀的,彻底的答案,唯一直接回答实际问题的人。保留部分可能需要工作,例如文字 ? 在查询部分 in 中很好,但在它之前是不可能的,我认为 @ 不属于这些列表中的任何一个。哦,最后一个字符串中的 %25 不是指 %7C 吗?
谢谢。好消息:示例中的 %25 是一个错字。直接从 RFC-2396 为“保留”语法描述添加了脚注。
这个答案还不错,但存在一些混淆和错误。您最初将不允许的字符和保留的字符(非常不同的东西)混为一谈,您在“不明智的”字符和其他不允许的字符(在 RFC 3986 中删除并且即使在 RFC 2396 中也与语法无关)之间做出了太多区分,并且您令人困惑地提供了一个列表所有保留字符作为“在查询组件内”保留的列表。
谢谢,并不是要将不允许的和保留的分组相同。更新了答案。恕我直言,RFC-2396 中的规则虽然较旧,但比 3986 中的更新规则更容易理解。答案更多地反映了哪些字符通常可能很麻烦,而不是确切地允许或不允许哪些上下文。
值得注意的是,在最近的版本(7.0.73+、8.0.39+、8.5.7+)中,Tomcat 已经开始拒绝带有 HTTP 400 错误的“不明智”类别字符的请求:“在请求目标中发现无效字符。有效字符在 RFC 7230 和 RFC 3986 中定义”
C
Community

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

https://en.wikipedia.org/wiki/Möbius_strip 或

https://zh.wikipedia.org/wiki/Wikipedia:关于中文维基百科/en。

首先,题外话。这些地址是什么?它们是有效的 URL 吗?

从历史上看,答案是“不”。根据 RFC 3986,从 2005 年开始,此类地址不是 URI(因此不是 URL,因为 URL are a type of URIs)。根据 2005 IETF 标准的术语,我们应该正确地称它们为 IRI(国际化资源标识符),如 RFC 3987 中定义的那样,它们在技术上不是 URI,但可以通过简单地对所有非 ASCII 字符进行百分比编码来转换为 URI。 IRI。

根据现代规范,答案是“是”。 WHATWG Living Standard 只是将以前称为“URI”或“IRI”的所有内容分类为“URL”。这使规范的术语与没有阅读规范的普通人如何使用“URL”一词保持一致,这是规范的goals之一。

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

根据“URL”的新含义,允许使用哪些字符?在 URL 的许多部分,例如查询字符串和路径,我们可以使用任意 "URL units",它们是

URL 代码点和百分比编码字节。

什么是“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 代码单元”中使用。)

我能发现规范允许使用该集合中任何 not 字符的唯一地方是在 host 中,其中 IPv6 地址包含在 [] 字符中。在 URL 的其他任何地方,要么允许 URL 单元,要么允许使用更严格的字符集。

旧 RFC 允许使用哪些字符?

为了历史起见,并且由于这里的答案中没有在其他地方充分探索,让我们检查一下在旧的规范对下是允许的。

首先,我们有两种 RFC 3986 reserved characters

:/?#[]@,它们是 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 block definitions,旧规范中的这些块选择似乎很奇怪且随意;这可能是因为自 RFC 3987 编写以来的十年中已添加了这些块。

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


"<>\^`{|} 不被禁止,它们被标记为不安全。但这些字符经常用于现实世界
@puchu 不,它们是被禁止的。自 RFC 1738(1994 年发布,二十多年前已被 RFC 2368 淘汰)以来,这些字符就没有使用“不安全”的名称,即使在那里,它也只是“禁止”的古怪同义词; RFC 1738 说“所有不安全的字符必须始终在 URL 中进行编码”。 (强调我的)。
也许这些符号在某些 rfc 完成时是被禁止的,但它们在现实世界中没有被编码,并且经常被旧客户端和服务器按原样使用。
我发现几个在 url 中使用 u007F(删除)字符的网络服务器。但我不能 100% 确定它们不是损坏的蜜罐之类的东西。
+1 用于实际回答问题而不是解释问题并回答另一个问题。来自 Google,我正在寻找一些可用于测试 URL 验证方法的无效字符。其他人正在回答如何编写 URL 验证方法...
C
Community

在您的补充问题中,您询问 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 更详细的解释。
URL 不是 URI 的子集。对于我见过的几乎解析器,[] 不是有效的 URI。这实际上让我在现实世界中搞砸了:stackoverflow.com/questions/11038967/…
@AdamGent URL 很大程度上是 URI 的子集。它们之间的唯一区别是它们是否描述了资源的位置——这是语义上的区别,而不是句法上的区别。如果您看到的将自己标记为“URI”解析器的解析器对方括号的处理与将自己标记为“URL”解析器的解析器不同,那么这纯属巧合,不是由 URL 和 URI 之间的任何差异引起的。
@Mark Amery 类似于说C++ 是C 的超集。这在很大程度上是但并不完全正确,因为(URL 和C)更老了,它们必须包含不太严格的行为。问题是 URL 解析器会解析无效 URI 的东西……我的意思是它们中的大多数(坦率地说,我已经厌倦了在这么多语言中指出这一点)这不是巧合,它是向后兼容的。我们可以同意 URL 规范至少更旧吗?
@MarkAmery 来自 Python、C#、Java 和一些 C 库,解析器将非常重视 Unwise 的 URI,但对 URL 库很好。那就是没有标志可以忽略 Unwise。我将不得不检查 URL 的 Rust 语言(因为它是为浏览器构建的,我很好奇它的作用)。大多数浏览器也会愉快地传递“[”,“]”。所以理论上就像我用 C/C++ 说的那样,它们是 sub/super,但事实并非如此。它高度依赖于对超/子集的规范和语义的解释。
C
Community

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

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

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


对于 Unicode 字符,Wikipedia 文章 Percent-encoding 说:“通用 URI 语法要求提供 URI 中字符数据表示的新 URI 方案实际上必须表示字符并且应该根据 UTF-8 将所有其他字符转换为字节,然后对这些值进行百分比编码。"
C
Community

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

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

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

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

使 RFC 3986 和 RFC 3987 与当代实施保持一致,并在此过程中废弃它们。

该文档将 URL code points 定义为:

ASCII 字母数字、“!”、“$”、“&”、“'”、“(”、“)”、“*”、“+”、“”、“-”、“.”、“/” , ":", ";", "=", "?", "@", "_", "~" 和 U+00A0 到 U+D7FF, U+E000 到 U+FDCF 范围内的代码点, U+FDF0 到 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,但不会通过像空格 "a b" 这样的字符的 URL

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

另请参阅:Unicode characters in URLs


P
Peter Mortensen

我需要选择字符来拆分字符串中的 URL,所以我决定创建一个我自己在 URL 中找不到的字符列表:

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

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


P
Peter Mortensen

这并不是您问题的真正答案,但验证 URL 确实是一个严肃的问题。您最好验证域名并将 URL 的查询部分保留。这就是我的经验。

您还可以使用 ping URL 并查看它是否会产生有效响应,但这对于如此简单的任务来说可能太多了。

检测URL的正则表达式很丰富,google一下:)


This answer 建议 URL 验证不是针对正则表达式的工作,而是针对特定于语言/平台的库
P
Peter Mortensen

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

您不能直接使用 RFC 1738、2396 或 3986。有许多旧的 HTTP 客户端和服务器允许更多字符。因此,我根据意外发布的网络服务器访问日志进行了研究:"GET URI HTTP/1.0" 200

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

\ { } < > | ` ^ "

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

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

请在 oghttp-request-collector 中阅读有关这项研究的更多信息。


是否有任何 API 可以从字符串中删除这些字符
B
BobMilton

我无法对上述答案发表评论,但想强调一点(在另一个答案中),允许字符在任何地方都不允许使用。例如,域名不能有下划线,所以 http://test_url.com 是无效的。


您的答案可以通过额外的支持信息得到改进。请edit添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。您可以找到有关如何写出好答案的更多信息in the help center
P
Peter Mortensen

我想出了几个用于 PHP 的正则表达式,它们可以将文本中的 URL 转换为锚标记。 (首先它将所有 www.url 转换为 http://,然后将所有带有 https?:// 的 URL 转换为 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) );


-1;除了它们都以某种身份涉及 URL 之外,这与所提出的问题无关。