63

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

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

对于获取客户端 IP 地址的任务,还有什么其他有用的东西吗?

4

7 回答 7

64

除此之外REMOTE_ADDRHTTP_X_FORWARDED_FOR还有一些其他的标头可以设置,例如:

  • 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

于 2009-05-27T14:47:55.050 回答
31

这取决于您网站的性质。

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

正如 Kosi 所说,要小心你在做什么 - IP 绝不是识别唯一访问者的可靠方法。

于 2009-02-09T10:41:38.830 回答
10

我已将 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)
    };
}
于 2012-03-27T04:38:33.523 回答
7

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

路上的问题是,有很多情况下 IP 并没有真正与客户端对齐:

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

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

于 2009-02-09T10:28:04.137 回答
2

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

于 2009-02-09T12:02:05.033 回答
1

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

它是一个得到广泛支持的IETF 草案标准:

大多数代理服务器都支持 X-Forwarded-For 字段,包括 Squid、Apache mod_proxy、Pound、HAProxy、Varnish 缓存、IronPort Web Security Appliance、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 的 Vital Security、NetApp NetCache、jetNEXUS、Crescendo Networks 的 Maestro、Web Adjuster 和 Websense Web Security Gateway。

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

于 2014-04-01T19:26:11.640 回答
0

从您的 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();
    }

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

于 2019-05-22T10:26:09.127 回答