【问题标题】:Getting the client IP address: REMOTE_ADDR, HTTP_X_FORWARDED_FOR, what else could be useful?获取客户端 IP 地址:REMOTE_ADDR、HTTP_X_FORWARDED_FOR,还有什么有用的?
【发布时间】:2010-10-06 08:56:02
【问题描述】:

我知道查看这两个变量是一种标准做法。当然,它们很容易被欺骗。我很好奇您多久可以期望这些值(尤其是HTTP_X_FORWARDED_FOR)包含真实信息,而不仅仅是被打乱或剥夺它们的值?

有这方面的经验或统计数据的人吗?

还有什么对获取客户端 IP 地址的任务有用吗?

【问题讨论】:

  • 请注意问题和答案都使用 HTTP_ 前缀,这是 ASP.NET v1.0-v4.x 的特定实现细节,当 HTTP 请求标头添加到 ServerVariables 集合时。另一个例子是 REMOTE_ADDR,它在 ASP.NET Core 中有自己的 API。 stackoverflow.com/questions/28664686/…

标签: asp.net http http-headers ip ip-address


【解决方案1】:

除了REMOTE_ADDRHTTP_X_FORWARDED_FOR之外,还有一些其他的headers可以设置如:

  • HTTP_CLIENT_IP
  • HTTP_X_FORWARDED_FOR 可以是逗号分隔的 IP 列表
  • HTTP_X_FORWARDED
  • HTTP_X_CLUSTER_CLIENT_IP
  • HTTP_FORWARDED_FOR
  • HTTP_FORWARDED

我发现以下网站上的代码很有用:
http://www.grantburton.com/?p=97

【讨论】:

【解决方案2】:

这取决于您网站的性质。

我碰巧在一些软件上工作,其中 IP 跟踪很重要,在合作伙伴网站使用的字段中,我猜大约 20% - 40% 的请求是可检测到的欺骗 IP 或标头空白,具体取决于一天中的时间和他们来自哪里。对于获得自然流量(即不是通过合作伙伴)的网站,我预计好的 IP 比例会更高。

正如 Kosi 所说,要小心处理此问题 - IP 绝不是识别唯一身份访问者的可靠方法。

【讨论】:

    【解决方案3】:

    我已将 Grant Burton 的 PHP 代码移植到可针对 HttpRequestBase 调用的 ASP.Net 静态方法。它可以选择跳过任何私有 IP 范围。

    public static class ClientIP
    {
        // based on http://www.grantburton.com/2008/11/30/fix-for-incorrect-ip-addresses-in-wordpress-comments/
        public static string ClientIPFromRequest(this HttpRequestBase request, bool skipPrivate)
        {
            foreach (var item in s_HeaderItems)
            {
                var ipString = request.Headers[item.Key];
    
            if (String.IsNullOrEmpty(ipString))
                continue;
    
            if (item.Split)
            {
                foreach (var ip in ipString.Split(','))
                    if (ValidIP(ip, skipPrivate))
                        return ip;
            }
            else
            {
                if (ValidIP(ipString, skipPrivate))
                    return ipString;
            }
        }
    
        return request.UserHostAddress;
    }
    
    private static bool ValidIP(string ip, bool skipPrivate)
    {
        IPAddress ipAddr;
    
        ip = ip == null ? String.Empty : ip.Trim();
    
        if (0 == ip.Length
            || false == IPAddress.TryParse(ip, out ipAddr)
            || (ipAddr.AddressFamily != AddressFamily.InterNetwork
                && ipAddr.AddressFamily != AddressFamily.InterNetworkV6))
            return false;
    
        if (skipPrivate && ipAddr.AddressFamily == AddressFamily.InterNetwork)
        {
            var addr = IpRange.AddrToUInt64(ipAddr);
            foreach (var range in s_PrivateRanges)
            {
                if (range.Encompasses(addr))
                    return false;
            }
        }
    
        return true;
    }
    
    /// <summary>
    /// Provides a simple class that understands how to parse and
    /// compare IP addresses (IPV4) ranges.
    /// </summary>
    private sealed class IpRange
    {
        private readonly UInt64 _start;
        private readonly UInt64 _end;
    
        public IpRange(string startStr, string endStr)
        {
            _start = ParseToUInt64(startStr);
            _end = ParseToUInt64(endStr);
        }
    
        public static UInt64 AddrToUInt64(IPAddress ip)
        {
            var ipBytes = ip.GetAddressBytes();
            UInt64 value = 0;
    
            foreach (var abyte in ipBytes)
            {
                value <<= 8;    // shift
                value += abyte;
            }
    
            return value;
        }
    
        public static UInt64 ParseToUInt64(string ipStr)
        {
            var ip = IPAddress.Parse(ipStr);
            return AddrToUInt64(ip);
        }
    
        public bool Encompasses(UInt64 addrValue)
        {
            return _start <= addrValue && addrValue <= _end;
        }
    
        public bool Encompasses(IPAddress addr)
        {
            var value = AddrToUInt64(addr);
            return Encompasses(value);
        }
    };
    
    private static readonly IpRange[] s_PrivateRanges =
        new IpRange[] { 
                new IpRange("0.0.0.0","2.255.255.255"),
                new IpRange("10.0.0.0","10.255.255.255"),
                new IpRange("127.0.0.0","127.255.255.255"),
                new IpRange("169.254.0.0","169.254.255.255"),
                new IpRange("172.16.0.0","172.31.255.255"),
                new IpRange("192.0.2.0","192.0.2.255"),
                new IpRange("192.168.0.0","192.168.255.255"),
                new IpRange("255.255.255.0","255.255.255.255")
        };
    
    
    /// <summary>
    /// Describes a header item (key) and if it is expected to be 
    /// a comma-delimited string
    /// </summary>
    private sealed class HeaderItem
    {
        public readonly string Key;
        public readonly bool Split;
    
        public HeaderItem(string key, bool split)
        {
            Key = key;
            Split = split;
        }
    }
    
    // order is in trust/use order top to bottom
    private static readonly HeaderItem[] s_HeaderItems =
        new HeaderItem[] { 
                new HeaderItem("HTTP_CLIENT_IP",false),
                new HeaderItem("HTTP_X_FORWARDED_FOR",true),
                new HeaderItem("HTTP_X_FORWARDED",false),
                new HeaderItem("HTTP_X_CLUSTER_CLIENT_IP",false),
                new HeaderItem("HTTP_FORWARDED_FOR",false),
                new HeaderItem("HTTP_FORWARDED",false),
                new HeaderItem("HTTP_VIA",false),
                new HeaderItem("REMOTE_ADDR",false)
        };
    }
    

    【讨论】:

    • 感谢您的代码。不过,它有几个问题。首先,ValidIP 中有一个额外的return false;。其次,IpRange 类并不真正处理 IPV6,因为 IPV6 地址是 128 位的。也许可以使用来自 .NET 4 的 System.Numerics.BigInteger,但也有可能私有范围对 IPV6 不太感兴趣(?)。
    • 哦,还有另一个问题:我认为您需要 request.ServerVariables,而不是检查 request.Headers 的标头。后者具有HTTP_X_FORWARDED_FOR 之类的键,而前者只是X-Forwarded-For
    • 如果这也支持RFC 7239 :D
    • Headers vs ServerVariables ?
    • @Kevin 你实现code 以支持 RFC 7239 吗?
    【解决方案4】:

    您的问题没有真正的答案,但是:
    在我看来,通常依赖客户端 IP 地址不是一个好的做法,因为它无法以独特的方式识别客户端。

    目前的问题是,在很多情况下 IP 并没有真正与客户端保持一致:

    • 代理/Webfilter(几乎所有东西都被破坏)
    • 匿名器网络(这里也没有机会)
    • NAT(内部 IP 对您不是很有用)
    • ...

    我无法提供任何关于有多少 IP 地址平均可靠的统计数据,但我可以告诉你的是,几乎不可能判断给定的 IP 地址是否是真实的客户端地址。 p>

    【讨论】:

    • 哪个是best practices以独特的方式识别客户清单不使用客户端IP地址
    【解决方案5】:

    IP +“用户代理”可能更适合唯一身份访问者。

    【讨论】:

    • 不,用户代理不是很多样化,而且被广泛欺骗
    • 被广泛欺骗,但通常它们不会因请求而异 - panopticlick.eff.org
    【解决方案6】:

    如果您使用代理,则应使用 X-Forwarded-For: http://en.wikipedia.org/wiki/X-Forwarded-For

    这是一个得到广泛支持的IETF draft standard

    大多数代理服务器都支持 X-Forwarded-For 字段, 包括 Squid、Apache mod_proxy、Pound、HAProxy、Varnish 缓存、 IronPort 网络安全设备、AVANU WebMux、ArrayNetworks、 Radware 的 AppDirector 和 Alteon ADC、ADC-VX 和 ADC-VA、F5 Big-IP、 Blue Coat ProxySG、Cisco Cache Engine、McAfee Web Gateway、Phion Airlock、Finjan's Vital Security、NetApp NetCache、jetNEXUS、Crescendo Networks 的 Maestro、Web Adjuster 和 Websense Web Security Gateway。

    如果没有,这里有几个我见过的其他常见标题:

    【讨论】:

      【解决方案7】:

      从你的 JS 文件中调用下面的操作方法(获取 ipv4 ip 地址)。

          [HttpGet]
          public string GetIP()
          {
              IPAddress[] ipv4Addresses = Array.FindAll(
                  Dns.GetHostEntry(string.Empty).AddressList,
                  a => a.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork);
              return ipv4Addresses.ToString();
          }
      

      保留断点后检查,并根据您的要求使用。 它对我来说工作正常。

      【讨论】:

        猜你喜欢
        • 2011-09-29
        • 2019-01-14
        • 1970-01-01
        • 2019-11-13
        • 1970-01-01
        • 1970-01-01
        • 2013-07-07
        • 2011-05-26
        • 2015-05-05
        相关资源
        最近更新 更多