【问题标题】:Private IP Address Identifier in Regular Expression正则表达式中的私有 IP 地址标识符
【发布时间】:2011-02-18 07:56:58
【问题描述】:

我想知道这是否是匹配以私有 IP 地址(Perl 风格的正则表达式)开头的字符串的最佳方式:

(^127\.0\.0\.1)|(^192\.168)|(^10\.)|(^172\.1[6-9])|(^172\.2[0-9])|(^172\.3[0-1])

非常感谢!

【问题讨论】:

  • 首先,您应该查看 RFC1918 以获得正确的列表。其次,我建议不涉及正则表达式的解决方案更容易维护。将 IP 地址转换为数字后,很容易将其与私有 IP 范围列表进行匹配。这也可以让您轻松使用公开可用的 bogon 列表,其中包含的内容远不止 RFC1918。
  • @derobert true,但对于 Tomcat 远程地址过滤器等用途,您需要正则表达式。
  • 在这种情况下认为^ 表示“不是”是一个常见的初学者错误,因此需要指出:表达式中的每个^ 只是将匹配项锚定到行首。在传统的正则表达式中,虽然 Perl 兼容 / PCRE 表达式与 (?!...)

标签: regex ip-address private


【解决方案1】:

这是 Mark Byers 解决方案的紧凑形式:

^(172\.(1[6-9]\.|2[0-9]\.|3[0-1]\.)|192\.168\.|10\.|127\.)

【讨论】:

    【解决方案2】:

    晚了 10 年。感谢Mark ByersbrampEdwardblueyeduser3177026JustinkarmakazeRon Maupin

    回答 TLDR

    仅开始

    请删除换行符 - 这只是为了便于阅读:

    ^(?:
    127\.|
    0?10\.|
    172\.0?1[6-9]\.|
    172\.0?2[0-9]\.|
    172\.0?3[01]\.|
    192\.168\.|
    169\.254\.|
    ::1|
    [fF][cCdD][0-9a-fA-F]{2}:|
    [fF][eE][89aAbB][0-9a-fA-F]:
    )
    

    括号开头的?:表示这个括号没有被捕获,可能会让它更快一点。

    请注意反斜杠。在 Postgres 例如您必须使用 E 字符串并且必须用反斜杠转义反斜杠 - 或者只使用 [.] 而不是 E'\\.'

    整个 IP(十进制,前导零可选,最多三位)

    请删除换行符 - 这只是为了便于阅读:

    \b(
    127\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)|
    0?10\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)|
    172\.0?1[6-9]\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)|
    172\.0?2[0-9]\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)|
    172\.0?3[01]\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)|
    192\.168\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)|
    169\.254\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)|
    ::1|
    [fF][cCdD][0-9a-fA-F]{2}(?:[:][0-9a-fA-F]{0,4}){0,7}|
    [fF][eE][89aAbB][0-9a-fA-F](?:[:][0-9a-fA-F]{0,4}){0,7}
    )
    (?:\/([789]|1?[0-9]{2}))?
    \b
    

    https://regex101.com/r/JCLOZL/14(备份:https://etherpad.wikimedia.org/p/JCLOZL

    详情

    IPv4

      127.0.0.0 to 127.255.255.255 is   127.0.0.0/8   # localhost, loopback etc.
       10.0.0.0 to  10.255.255.255 is    10.0.0.0/8   # approximately/formerly class A
     172.16.0.0 to  172.31.255.255 is  172.16.0.0/12  # approximately/formerly class B
    192.168.0.0 to 192.168.255.255 is 192.168.0.0/16  # approximately/formerly class C
    169.254.0.0 to 169.254.255.255 is 169.254.0.0/16  # link-local addresses since 2005
    

    例子:

    172.17.50.33 or more explicit:
    172.17.50.33/32
    

    我不确定斜线后面的部分是否可以有前导零(不太可能),但 IP 可以有前导零。

    带前导零的 IPv4(十进制)

    127.000.000.000 to 127.255.255.255 is 127.000.000.000/8   # localhost, loopback etc.
    010.000.000.000 to 010.255.255.255 is 010.000.000.000/8   # approx/formerly class A
    172.016.000.000 to 172.031.255.255 is 172.016.000.000/12  # approx/formerly class B
    192.168.000.000 to 192.168.255.255 is 192.168.000.000/16  # approx/formerly class C
    169.254.000.000 to 169.254.255.255 is 169.254.000.000/16  # link-local addresses
    

    例子:

    172.017.050.033 or more explicit:
    172.017.050.033/32
    

    带前导零的 IPv4(八进制)

    我的正则表达式不支持。为了使您的程序完美,请检查并警告用户八进制 IP 地址和/或超过三位数!

    0177.0000.0000.0000 to 0177.0377.0377.0377 is 0177.0000.0000.0000/8   # loopback
    0012.0000.0000.0000 to 0012.0377.0377.0377 is 0012.0000.0000.0000/8   # A
    0254.0020.0000.0000 to 0254.0037.0377.0377 is 0254.0020.0000.0000/12  # B
    0300.0250.0000.0000 to 0300.0250.0377.0377 is 0300.0250.0000.0000/16  # C
    0251.0376.0000.0000 to 0251.0376.0377.0377 is 0251.0376.0000.0000/16  # link-local
    

    例子:

    0254.0021.0062.0041 or more explicit:
    0254.0021.0062.0041/32
    

    是的,172.017.050.033 在不同的工具上与0254.0021.0062.0041 相同。在 macOS 上使用ping 测试。

    当然,您还可以在某些工具中将十进制(无前导零)与八进制(至少一个前导零)混合使用。 :S

    IPv6

    fc00:0000:… to fdff:ffff:… is fc00::/7
    fe80:0000:… to febf:ffff:… is fe80::/10  # link-local addresses
    

    这可能不是您想要的,因为:

    私有 IPv4 地址由 RFC 1918 定义,私有 Internet 的地址分配,并且这些地址在多个网络中使用。 IPv6 ULA 地址是唯一的(ULA 中的“U”),不能在多个地方重复使用,这就是它们需要在 Global ID 中具有 40 个随机位的原因,从而对唯一性有很高的期望。

    来自公共 Internet 的所有地址都将在 2000::3fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff (2000::/3) 的范围内。 您需要正确屏蔽地址以查看它是否在公共范围内,然后对公共范围内的非公共范围执行相同的操作。如果它通过了公共范围测试,则它必须在公共范围内的非公共范围内失败。如果它通过了所有这些,它就是一个公共地址。 IOT 设备还获得全球(公共)IPv6 地址。 IPv6 的重点是有足够的地址,每台设备上的每个接口都可以获得一个全局 IPv6 地址,从而恢复 IP 最初的设计方式(不再需要解决 NAT 问题)。

    您的网络服务器内部的一些地址将是链接本地地址 (fe80::/10),并且可以为以下流量分配 ULA 地址(您可以从 fd00::/8 分配其中的fc00::/7,但有一定的限制)绝不允许在公共互联网上使用。

    例子:

    fdff:1234:abcd:5678:effe:9098:dcba:7654 or more explicit:
    fdff:1234:abcd:5678:effe:9098:dcba:7654/128
    

    不完整的链接列表

    当我来到这里时,我不知道:

    【讨论】:

      【解决方案3】:

      如果超过一位,您需要一个结束分隔符来获取整个第 4 个八位字节。

      ^(10.([0-9][0-9]?|[0-1][0-9]?[0-9]?|2[0-4]?[0-9] ?|25[0-5])|172.(1[6-9]|2[0-9]|3[0-1])|192.168).([0-9][0-9]? |[0-1][0-9]?[0-9]?|2[0-4]?[0-9]?|25[0-5]).([0-9][0- 9]?|[0-1][0-9]?[0-9]?|2[0-4]?[0-9]?|25[0-5])$

      【讨论】:

        【解决方案4】:

        我已经生成了这个

        A 类网络的正则表达式:

        (10)(\.([2]([0-5][0-5]|[01234][6-9])|[1][0-9][0-9]|[1-9][0-9]|[0-9])){3}

        B 类网络的正则表达式:

        (172)\.(1[6-9]|2[0-9]|3[0-1])(\.(2[0-4][0-9]|25[0-5]|[1][0-9][0-9]|[1-9][0-9]|[0-9])){2}

        C 类网络的正则表达式:

        (192)\.(168)(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])){2}

        如果您遇到任何错误,请告诉我

        如果您确定您的输出(例如 netstat)并且您不需要检查 IP 地址的有效性,因为它已经完成,那么您可以使用此公式捕获私有 IP 地址

        grep -P "(10.|192.168|172.1[6-9].|172.2[0-9].|172.3[01].).* "

        【讨论】:

        • 仅将[2][0-5][0-5] 用于 200 到 255 的范围,206 到 209 和 216 到 219 等数字将不匹配。您必须将其替换为 ([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])
        • 这是用于什么语言的?我无法让它工作,regex101.com/r/jdVmny/1
        【解决方案5】:

        这是我在 python 中使用的:

        rfc1918 = re.compile('^(10(\.(25[0-5]|2[0-4][0-9]|1[0-9]{1,2}|[0-9]{1,2})){3}|((172\.(1[6-9]|2[0-9]|3[01]))|192\.168)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{1,2}|[0-9]{1,2})){2})$')
        

        如果您愿意,可以删除 ^ 和/或 $ 锚。

        我更喜欢上面的正则表达式,因为它会清除无效的八位字节(任何高于 255 的字节)。

        示例用法:

        if rfc1918.match(ip):
            print "ip is private"
        

        【讨论】:

          【解决方案6】:
               //RegEx to check for the following ranges. IPv4 only
                   //172.16-31.xxx.xxx
                   //10.xxx.xxx.xxx
                   //169.254.xxx.xxx
                   //192.168.xxx.xxx
          
               var regex = /(^127\.)|(^(0)?10\.)|(^172\.(0)?1[6-9]\.)|(^172\.(0)?2[0-9]\.)|(^172\.(0)?3[0-1]\.)|(^169\.254\.)|(^192\.168\.)/;
          

          【讨论】:

            【解决方案7】:

            使用pattern.matcher,这种模式的速度提高了 10% 以上:

            ^1((0)|(92\\.168)|(72\\.((1[6-9])|(2[0-9])|(3[0-1])))|(27))\\.
            

            【讨论】:

            • 我做了一些测试,但它似乎慢了 100%。您能否提供一些测试来支持这一点?
            【解决方案8】:

            我假设你想匹配这些范围:

            127. 0.0.0 – 127.255.255.255 127.0.0.0 /8 10. 0.0.0 – 10.255.255.255 10.0.0.0 /8 172. 16.0.0 – 172. 31.255.255 172.16.0.0 /12 192.168.0.0 - 192.168.255.255 192.168.0.0 /16

            您缺少一些会导致它接受的点,例如172.169.0.0,即使这不应该被接受。我已经在下面修复了它。删除新行,只是为了便于阅读。

            (^127\.)|
            (^10\.)|
            (^172\.1[6-9]\.)|(^172\.2[0-9]\.)|(^172\.3[0-1]\.)|
            (^192\.168\.)
            

            另请注意,这假定 IP 地址已经过验证 - 它接受 10.foobar 之类的内容。

            【讨论】:

            • 如果你想从谷歌分析跟踪中排除私人 IP 地址,使用这个:gist.github.com/1000402
            • 第一行应该是 (^127\.)|
            • Java 版本:'(^127\\.0\\.0\\.1)|(^10\\.)|(^172\\.1[6-9]\\.)|(^172\\.2[0-9]\\.)|(^172\\.3[0-1]\\.)|(^192\\.168\\.)'
            • 更复杂但更准确:(^127\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$)|(^10\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$)|(^172\.1[6-9]{1}[0-9]{0,1}\.[0-9]{1,3}\.[0-9]{1,3}$)|(^172\.2[0-9]{1}[0-9]{0,1}\.[0-9]{1,3}\.[0-9]{1,3}$)|(^172\.3[0-1]{1}[0-9]{0,1}\.[0-9]{1,3}\.[0-9]{1,3}$)|(^192\.168\.[0-9]{1,3}\.[0-9]{1,3}$)
            • 自动链接本地私有地址 169.254.*.* 也应该在这个集合中。写得很紧凑:^(10|127|169\.254|172\.1[6-9]|172\.2[0-9]|172\.3[0-1]|192\.168)\.
            【解决方案9】:

            这与 Mark 的正确答案相同,但现在包括 IPv6 私有地址。

            /(^127\.)|(^192\.168\.)|(^10\.)|(^172\.1[6-9]\.)|(^172\.2[0-9]\.)|(^172\.3[0-1]\.)|(^::1$)|(^[fF][cCdD])/
            

            【讨论】:

            • 不好 - 根据维基百科 (en.wikipedia.org/wiki/Unique_local_address),块 (fc00::/7) 是为唯一本地地址保留的。此 Regex 仅将 localhost 地址 (::1) 添加到已接受的解决方案中。
            • 根据github.com/rails/rails/pull/12651/files 的正则表达式将是^[fF][cCdD] - 我正在相应地编辑答案。
            • 脚本开头和结尾处的/ 字符表明您正在使用某种语言(如Javascript)中的表达式,但这在其他语言中无效。尽管如此,一个较短的版本(没有斜线)是^(127\.)|(192\.168\.)|(10\.)|(172\.1[6-9]\.)|(172\.2[0-9]\.)|(172\.3[0-1]\.)|(::1$)|([fF][cCdD])
            • @MarianoRuiz 我相信如果你在开头有插入符号,你需要一个捕获组,否则你最终匹配 127. 在开头,其余组在字符串。
            【解决方案10】:

            如果您正在寻找 system.net defaultProxy 和 proxy bypasslist 配置,该配置使用外部代理但内部主机使用直接连接(可以使用一些 ipv6 支持)...

            <system.net>
              <defaultProxy enabled="true">
                <proxy proxyaddress="http://proxycluster.privatedomain.net:8080" bypassonlocal="True"  />
                <bypasslist>
                  <!-- exclude local host -->
                  <add address="^(http|https)://localhost$" />
                  <!-- excludes *.privatedomain.net -->
                  <add address="^(http|https)://.*\.privatedomain\.net$" />
                  <!-- excludes simple host names -->
                  <add address="^(http|https)://[a-z][a-z0-9\-_]*$" />
                  <!-- exclude private network addresses 192.168, 172.16..31 through 31, 127.* etc. -->
                  <add address="^(http|https)://((((127)|(10))\.[0-9]+\.[0-9]+\.[0-9]+)|(((172\.(1[6-9]|2[0-9]|3[0-1]))|(192\.168))\.[0-9]+\.[0-9]+))$"/>
                </bypasslist>
              </defaultProxy>
              <connectionManagement>
                <add address="*" maxconnection="10" />
              </connectionManagement>
            </system.net>
            

            【讨论】:

              【解决方案11】:

              看起来不错。就个人而言,我会将第一个更改为:

              ^127\.0 
              

              有了这个:(^127\.0\.0\.1) 你正在寻找以127.0.0.1 开头的任何东西,并且会错过127.0.0.2*127.0.2.*127.0.* 等。

              【讨论】:

              • 我知道这很旧,但这个问题在我的谷歌搜索中是第一或第二。我相信整个 127.0.0.0/8 块是为环回保留的,所以 0 甚至不应该在那里。
              【解决方案12】:

              如果您决定接受我的评论,建议您不要使用正则表达式。在 Perl 中未经测试(但可能有效,或至少接近):

              @private = (
                  {network => inet_aton('127.0.0.0'),   mask => inet_aton('255.0.0.0')   },
                  {network => inet_aton('192.168.0.0'), mask => inet_aton('255.255.0.0') },
                  # ...
              );
              
              $ip = inet_aton($ip_text);
              if (grep $ip & $_->{mask} == $_->{network}, @private) {
                  # ip address is private
              } else {
                  # ip address is not private
              }
              

              现在请注意@private 只是数据,您可以轻松更改它。或从Cymru Bogon Reference 即时下载。

              edit:在我看来,请求 Perl 正则表达式并不意味着您知道 Perl,所以关键行是“grep”,它只是在每个私有地址范围内循环。你把你的IP,按位和它与网络掩码,并与网络地址进行比较。如果相等,则为该专用网络的一部分。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2011-06-20
                • 2017-03-15
                • 2015-11-22
                • 2016-02-07
                • 2013-02-21
                相关资源
                最近更新 更多