我想验证给定的字符串是 URL。匹配文本中的 URL 也很好,但不是必需的。我已经搜索并进行了实验,但到目前为止,我还没有找到可以满足这些要求的东西:
<a href="javascript:alert(document.cookie)">clickme</a>
是一个有效的 HTML 元素,并且至少在某些浏览器中确实有效(引发警报等)。我担心如果我允许任意方案(见下文),它可能会损害安全性(如前所述,例如,此处:What is the best regular expression to check if a string is a valid URL?)。 一个。我要接受http://fr.wikipedia.org/wiki/Français ,由于非英文字符,这是非标准的,但被我的引用浏览器 IE(7+) 和 Chrome 接受。
湾。我要接受http://fr.wikipedia.org/wiki/Fran%c3%a7ais ,这是非标准的,因为百分比编码十六进制应该是大写的,但又被 IE 和 Chrome 接受。我想我可以做一个不区分大小写的匹配——你能想到什么缺点吗?
C。我要接受http://localhost/localpath/servlet#action?param=value ,这是非标准的,因为片段部分(从“#”到结尾)不应包含“?”和其他字符,但有些应用程序会生成此类 URL,并且浏览器会接受它们。
d。我想接受任何方案/协议(protocol)的 URL(不仅仅是 http、https 和 ftp),因为我集成的各种应用程序及其用户可能需要传递这样的 URL。我可以禁止 'javascript:' 并允许其他一切;如果您认为这会损害安全性,请说出来。
在 SO 和其他地方有很多关于这个主题的问题,但我没有找到一个可以满足我所有要求的正则表达式。例子:
谢谢! :-)
最佳答案
Must accept URLs which are used in practice, and not only standard-compliant URLs
实际上,URI 规范非常自由,并且允许出于兼容性原因通常要排除的结构......
I want to accept http://fr.wikipedia.org/wiki/Français, which is non-standard
它不是一个URI,但它是一个相当标准的IRI .
- non-standard because percent-encoding hex should be uppercase
- non-standard because the fragment part (from '#' to the end) should not include '?'
根据 URI 标准,这两者都是完全可以接受的。 RFC 3986 建议但不要求在创建百分比编码时使用大写字母。
I can forbid 'javascript:' and allow everything else; if you think this would compromise security please say so.
它会。不幸的是,URI 方案命名空间中有多个潜在危险的添加,并且毫无疑问将来会继续存在。此外,使用编码字符和控制字符可能会规避黑名单功能。
此外,任意方案匹配意味着您在文本中检测地址的次要目标将在大多数使用冒号的情况下产生误报。
白名单是唯一可行的方法,因此您只需根据具体情况手动允许每个新方案。这需要一些小心;例如
data:
方案似乎无害且有用,但可能遭受与 javascript:
相同的 XSS 问题.您还需要了解有关每个方案的一些信息。像
http
这样的方案和 ftp
具有“基于服务器的命名权限”:它们可以在该主机中包含单独的主机名和资源路径;此外,您可能要求它们是绝对 URI。如果您想允许文件 URI,您必须检查它是否是无主机的 (file:///
)。对于其他方案,URI 标准本身可能没有具体的语法要求,但可能有其他限制,例如 mailto:
必须使用有效的电子邮件地址。Giant regex (I ask myself if all browsers and frameworks I use can handle this size) which appears to be very comprehensive
这在 JavaScript 中不起作用,因为它具有不受支持的
\x{code point}
句法。此外,像 JavaScript 这样的语言,其正则表达式引擎根据 UTF-16 代码单元而不是完整的 Unicode 代码点工作,将无法处理 BMP 之外的字符范围。您必须更换长
\x{A0}...\x{1FFFD}
像 \u00A0-\uFFFD
这样更简单的组,然后分别检查无效的代理对以及 0xnnFFFE–F 非字符,如果您关心这些(可能不关心)。可以说,在进行 IRI 验证之前,您可能已经在一般输入扫描级别上删除了任何错误的代理项和非字符;没有理由在任何文本输入中允许它们。在单独的步骤中执行此操作比尝试将所有内容硬塞到单个正则表达式中更有意义。
替换后,引用的正则表达式中最长的部分是试图验证数字 IP 地址的非常长的数字检查字符串。这是正则表达式根本不擅长的事情。我会强烈考虑不要打扰 IPv6 和 future 的 IPv6 数字地址:即使假设 IPv6 很快得到广泛采用,在可预见的 future 也不会有人使用它们。 (你甚至想允许指向数字地址的链接吗?取决于你的应用程序在做什么,但通常不是。)
您还可以考虑禁止 userinfo@ 主机名前缀(因为它们传统上除了欺骗攻击之外没有用处)和百分比编码的主机名(因为它们没有任何用途,因为 Punycode 的存在,并且在某些浏览器中不起作用) .
因此,IRI 验证没有一个单一的答案,但您可以从这里开始:
(
https?://
(
([0-9]{1-3}(\.[0-9]{1-3}){3})|
([-0-9a-z\u00A0-\uFFFD]{1-63}(\.[-0-9a-z\u00A0-\uFFFD]{1-63})*)
)
(:[0-9]+)?/
(
%[0-9a-f][0-9a-f]|
[-._!$&'()*+,:;=@~0-9a-z\u00A0-\uFFFD/?#]
)*
)|(
ftp:// // same again but with no ?query
... // or port number
)|(
mailto: // specify requirements for
... // other accepted schemes
)
(假定不区分大小写。这应用了不属于 URI 规范本身的 DNS 约束,尽管不完整,因为它不检查 DNS 标签中的前导/尾随
-
或 IPv4 八位字节中的数字范围。验证电子邮件地址留给读者作为练习,因为它本身就是一项艰巨的任务,如果您想严格执行正则表达式,则不适合。)
关于javascript - 真实世界 URL 的 URL 验证正则表达式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8869293/