【问题标题】:How do you determine if an IP address is private, in Python?在 Python 中,如何确定 IP 地址是否是私有的?
【发布时间】:2010-10-16 00:25:37
【问题描述】:

在 Python 中,确定 IP 地址(例如,'127.0.0.1''10.98.76.6')是否在 private network 上的最佳方法是什么?代码听起来并不难写。但是可能存在比立即明显更多的边缘情况,并且需要考虑 IPv6 支持等。是否有现有的库可以做到这一点?

【问题讨论】:

    标签: python network-programming ip-address


    【解决方案1】:

    从 Python 3.3 开始,您可以使用标准库中的 ipaddress module

    >>> import ipaddress
    >>> ipaddress.ip_address('192.168.0.1').is_private
    True
    

    如果使用 Python 2.6 或更高版本,我强烈建议使用a backport of this module

    【讨论】:

      【解决方案2】:

      查看IPy 模块。如果有一个函数iptype() 似乎可以做你想做的事:

      >>> from IPy import IP
      >>> ip = IP('127.0.0.0/30')
      >>> ip.iptype()
      'PRIVATE'
      

      【讨论】:

      • 正如对公共 IP 的预期,字符串是“PUBLIC”。有一些意想不到的结果:print(IP('127.0.0.1').iptype()) 产生PRIVATEprint(IP('::1').iptype()) 产生 LOOPBACKprint(IP('2001:0658:022a:cafe:0200::1').iptype()) 产生 ALLOCATED RIPE NCC
      • 确实有一些意想不到的结果:print(IP('0.0.0.0/0').iptype()) 产生“PRIVATE”,而我希望“整个 IPv4 互联网”是 PUBLIC...
      • 我应该用它来预防SSRF吗?
      • 你打算如何使用它来防止 SSRF ?
      【解决方案3】:

      您可以使用 https://www.rfc-editor.org/rfc/rfc1918https://www.rfc-editor.org/rfc/rfc3330。如果你有 127.0.0.1,你只需要用掩码 & 它(比如说255.0.0.0),看看该值是否与任何专用网络的network address 匹配。所以使用inet_pton 你可以这样做:127.0.0.1 & 255.0.0.0 = 127.0.0.0

      下面的代码说明了这一点:

      from struct import unpack
      from socket import AF_INET, inet_pton
      
      def lookup(ip):
          f = unpack('!I',inet_pton(AF_INET,ip))[0]
          private = (
              [ 2130706432, 4278190080 ], # 127.0.0.0,   255.0.0.0   https://www.rfc-editor.org/rfc/rfc3330
              [ 3232235520, 4294901760 ], # 192.168.0.0, 255.255.0.0 https://www.rfc-editor.org/rfc/rfc1918
              [ 2886729728, 4293918720 ], # 172.16.0.0,  255.240.0.0 https://www.rfc-editor.org/rfc/rfc1918
              [ 167772160,  4278190080 ], # 10.0.0.0,    255.0.0.0   https://www.rfc-editor.org/rfc/rfc1918
          ) 
          for net in private:
              if (f & net[1]) == net[0]:
                  return True
          return False
      
      # example
      print(lookup("127.0.0.1"))
      print(lookup("192.168.10.1"))
      print(lookup("10.10.10.10"))
      print(lookup("172.17.255.255"))
      # outputs True True True True
      

      另一种实现是计算所有私有块的 int 值:

      from struct import unpack
      from socket import AF_INET, inet_pton
      
      lookup = "127.0.0.1"
      f = unpack('!I',inet_pton(AF_INET,lookup))[0]
      private = (["127.0.0.0","255.0.0.0"],["192.168.0.0","255.255.0.0"],["172.16.0.0","255.240.0.0"],["10.0.0.0","255.0.0.0"])
      for net in private:
          mask = unpack('!I',inet_aton(net[1]))[0]
          p = unpack('!I',inet_aton(net[0]))[0]
          if (f & mask) == p:
              print lookup + " is private"
      

      【讨论】:

        【解决方案4】:

        这是@Kurt 建议的正则表达式方法的固定版本,包括@RobEvans 推荐的修复方法

        • ^127.\d{1,3}.\d{1,3}.\d{1,3}$

        • ^10.\d{1,3}.\d{1,3}.\d{1,3}$

        • ^192.168.\d{1,3}.\d{1,3}$

        • ^172.(1[6-9]|2[0-9]|3[0-1]).[0-9]{1,3}.[0-9]{1,3} $

           def is_ip_private(ip):
          
               # https://en.wikipedia.org/wiki/Private_network
          
               priv_lo = re.compile("^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$")
               priv_24 = re.compile("^10\.\d{1,3}\.\d{1,3}\.\d{1,3}$")
               priv_20 = re.compile("^192\.168\.\d{1,3}.\d{1,3}$")
               priv_16 = re.compile("^172.(1[6-9]|2[0-9]|3[0-1]).[0-9]{1,3}.[0-9]{1,3}$")
          
               res = priv_lo.match(ip) or priv_24.match(ip) or priv_20.match(ip) or priv_16.match(ip)
               return res is not None
          

        这不会是 kubernetes 内部使用的 100.x.x.x 范围

        【讨论】:

        • +1,我建议返回一个布尔“包装器”,以更好地反映函数名称:return (priv_lo.match(ip) or priv_24.match(ip) or priv_20.match(ip ) 或 priv_16.match(ip)) 不是无
        【解决方案5】:

        在问了这个问题几天后,我发现了这个 Google 项目 ipaddr-py,它在确定地址是否为私有 (is_rfc1918) 方面似乎具有一些相同的功能。显然这将成为 Python 3.1 中的标准。

        【讨论】:

        【解决方案6】:

        我在cuckoo找到这个。不需要安装新模块。只需导入两个内置模块:socket和struct。并使用下面的函数。

        def _is_private_ip(self, ip):
            """Check if the IP belongs to private network blocks.
            @param ip: IP address to verify.
            @return: boolean representing whether the IP belongs or not to
                     a private network block.
            """
            networks = [
                "0.0.0.0/8",
                "10.0.0.0/8",
                "100.64.0.0/10",
                "127.0.0.0/8",
                "169.254.0.0/16",
                "172.16.0.0/12",
                "192.0.0.0/24",
                "192.0.2.0/24",
                "192.88.99.0/24",
                "192.168.0.0/16",
                "198.18.0.0/15",
                "198.51.100.0/24",
                "203.0.113.0/24",
                "240.0.0.0/4",
                "255.255.255.255/32",
                "224.0.0.0/4",
            ]
        
            for network in networks:
                try:
                    ipaddr = struct.unpack(">I", socket.inet_aton(ip))[0]
        
                    netaddr, bits = network.split("/")
        
                    network_low = struct.unpack(">I", socket.inet_aton(netaddr))[0]
                    network_high = network_low | 1 << (32 - int(bits)) - 1
        
                    if ipaddr <= network_high and ipaddr >= network_low:
                        return True
                except Exception,err:
                    continue
        
            return False
        

        【讨论】:

        • 此检查失败示例如果 ip : 172.29.0.1
        【解决方案7】:

        如果你想避免导入一个模块,你可以应用一个简单的正则表达式:

        • ^127.\d{1,3}.\d{1,3}.\d{1,3}$
        • ^10.\d{1,3}.\d{1,3}.\d{1,3}$
        • ^192.168.\d{1,3}$
        • ^172.(1[6-9]|2[0-9]|3[0-1]).[0-9]{1,3}.[0-9]{1,3} $

        【讨论】:

        • 不应该是那些。字符通过 \ 转义。 ?我猜这个模式仍然可以工作。
        • 那个正则表达式对我不起作用,但如果你用 {1,3} 替换 {123} 就可以了。
        • 我不同意 - 它可能有效,但并不简单。如果我查看所有这些特殊字符等,据我所知,“简单”一词不适用于该正则表达式……您能否阅读,快速记住它并在不看它的情况下再次写下来(并且不重构根据您应用的规则)?如果有什么问题/改变了,你能一眼看出吗?所以,恕我直言,是的,如果您有原因或限制使添加外部代码成为不可能/不需要的,它可以工作,但它是一个复杂且难以调试/理解/记忆/验证的表达式。
        猜你喜欢
        • 2016-05-24
        • 1970-01-01
        • 2011-02-05
        • 2012-11-28
        • 2013-10-23
        • 2012-12-26
        • 1970-01-01
        • 2015-11-03
        • 2016-10-21
        相关资源
        最近更新 更多