草庐IT

javascript - 真实世界 URL 的 URL 验证正则表达式

coder 2025-03-28 原文

我想验证给定的字符串是 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?)。
  • 必须在 JavaScript 中正常工作。
  • 如果它在 Java 中也能同样工作,那就太好了——我正在 GWT 中开发,所以这很好,但不是绝对必要的。
  • 必须接受实际使用的 URL,而不仅仅是符合标准的 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 和其他地方有很多关于这个主题的问题,但我没有找到一个可以满足我所有要求的正则表达式。例子:
  • Regex in GWT to match URLs -- 非常好和简单的正则表达式,但不接受非标准的 URL。我可以处理方案部分和百分比编码区分大小写,但不能处理其他问题。
  • https://stackoverflow.com/a/190405/96929 -- 巨型正则表达式(我问自己是否我使用的所有浏览器和框架都可以处理这个大小),它看起来非常全面,但说它符合标准,我无法判断它的正面或反面。

  • 谢谢! :-)

    最佳答案

    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/

    有关javascript - 真实世界 URL 的 URL 验证正则表达式的更多相关文章

    1. ruby-on-rails - 如何验证 update_all 是否实际在 Rails 中更新 - 2

      给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru

    2. ruby - 具有身份验证的私有(private) Ruby Gem 服务器 - 2

      我想安装一个带有一些身份验证的私有(private)Rubygem服务器。我希望能够使用公共(public)Ubuntu服务器托管内部gem。我读到了http://docs.rubygems.org/read/chapter/18.但是那个没有身份验证-如我所见。然后我读到了https://github.com/cwninja/geminabox.但是当我使用基本身份验证(他们在他们的Wiki中有)时,它会提示从我的服务器获取源。所以。如何制作带有身份验证的私有(private)Rubygem服务器?这是不可能的吗?谢谢。编辑:Geminabox问题。我尝试“捆绑”以安装新的gem..

    3. ruby-on-rails - 如果为空或不验证数值,则使属性默认为 0 - 2

      我希望我的UserPrice模型的属性在它们为空或不验证数值时默认为0。这些属性是tax_rate、shipping_cost和price。classCreateUserPrices8,:scale=>2t.decimal:tax_rate,:precision=>8,:scale=>2t.decimal:shipping_cost,:precision=>8,:scale=>2endendend起初,我将所有3列的:default=>0放在表格中,但我不想要这样,因为它已经填充了字段,我想使用占位符。这是我的UserPrice模型:classUserPrice回答before_val

    4. ruby 正则表达式 - 如何替换字符串中匹配项的第 n 个实例 - 2

      在我的应用程序中,我需要能够找到所有数字子字符串,然后扫描每个子字符串,找到第一个匹配范围(例如5到15之间)的子字符串,并将该实例替换为另一个字符串“X”。我的测试字符串s="1foo100bar10gee1"我的初始模式是1个或多个数字的任何字符串,例如,re=Regexp.new(/\d+/)matches=s.scan(re)给出["1","100","10","1"]如果我想用“X”替换第N个匹配项,并且只替换第N个匹配项,我该怎么做?例如,如果我想替换第三个匹配项“10”(匹配项[2]),我不能只说s[matches[2]]="X"因为它做了两次替换“1fooX0barXg

    5. ruby-on-rails - 如何验证非模型(甚至非对象)字段 - 2

      我有一个表单,其中有很多字段取自数组(而不是模型或对象)。我如何验证这些字段的存在?solve_problem_pathdo|f|%>... 最佳答案 创建一个简单的类来包装请求参数并使用ActiveModel::Validations。#definedsomewhere,atthesimplest:require'ostruct'classSolvetrue#youcouldevencheckthesolutionwithavalidatorvalidatedoerrors.add(:base,"WRONG!!!")unlesss

    6. ruby-on-rails - rails : save file from URL and save it to Amazon S3 - 2

      从给定URL下载文件并立即将其上传到AmazonS3的更直接的方法是什么(+将有关文件的一些信息保存到数据库中,例如名称、大小等)?现在,我既不使用Paperclip,也不使用Carrierwave。谢谢 最佳答案 简单明了:require'open-uri'require's3'amazon=S3::Service.new(access_key_id:'KEY',secret_access_key:'KEY')bucket=amazon.buckets.find('image_storage')url='http://www.ex

    7. ruby - 如何使用 Ruby aws/s3 Gem 生成安全 URL 以从 s3 下载文件 - 2

      我正在编写一个小脚本来定位aws存储桶中的特定文件,并创建一个临时验证的url以发送给同事。(理想情况下,这将创建类似于在控制台上右键单击存储桶中的文件并复制链接地址的结果)。我研究过回形针,它似乎不符合这个标准,但我可能只是不知道它的全部功能。我尝试了以下方法:defauthenticated_url(file_name,bucket)AWS::S3::S3Object.url_for(file_name,bucket,:secure=>true,:expires=>20*60)end产生这种类型的结果:...-1.amazonaws.com/file_path/file.zip.A

    8. ruby-on-rails - 如何将验证与模型分开 - 2

      我有一些非常大的模型,我必须将它们迁移到最新版本的Rails。这些模型有相当多的验证(User有大约50个验证)。是否可以将所有这些验证移动到另一个文件中?说app/models/validations/user_validations.rb。如果可以,有人可以提供示例吗? 最佳答案 您可以为此使用关注点:#app/models/validations/user_validations.rbrequire'active_support/concern'moduleUserValidationsextendActiveSupport:

    9. ruby-on-rails - 跳过状态机方法的所有验证 - 2

      当我的预订模型通过rake任务在状态机上转换时,我试图找出如何跳过对ActiveRecord对象的特定实例的验证。我想在reservation.close时跳过所有验证!叫做。希望调用reservation.close!(:validate=>false)之类的东西。仅供引用,我们正在使用https://github.com/pluginaweek/state_machine用于状态机。这是我的预订模型的示例。classReservation["requested","negotiating","approved"])}state_machine:initial=>'requested

    10. ruby - 如何在 Rails 4 中使用表单对象之前的验证回调? - 2

      我有一个服务模型/表及其注册表。在表单中,我几乎拥有服务的所有字段,但我想在验证服务对象之前自动设置其中一些值。示例:--服务Controller#创建Action:defcreate@service=Service.new@service_form=ServiceFormObject.new(@service)@service_form.validate(params[:service_form_object])and@service_form.saverespond_with(@service_form,location:admin_services_path)end在验证@ser

    随机推荐