如何从 HTTP 标头获取客户端 IP 地址?

发布于 2024-07-13 12:58:37 字数 178 浏览 6 评论 0原文

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

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

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

I understand it's a standard practice to look at both these variables. Of course they can easily be spoofed. I'm curious how often can you expect these values (especially the HTTP_X_FORWARDED_FOR) to contain genuine information and not just be scrambled or have their values stripped away?

Anyone with the experience or statistics on this stuff?

Is there anything else that can be useful for the task of getting the client's IP address?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

春庭雪 2024-07-20 12:58:39

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

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

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

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

No real answer to your question but:
Generally relying on the clients IP address is in my opinion not a good practice as it is not usable to identify clients in a unique fashion.

Problems on the road are that there are quite a lot scenarios where the IP does not really align to a client:

  • Proxy/Webfilter (mangle almost everything)
  • Anonymizer network (no chance here either)
  • NAT (an internal IP is not very useful for you)
  • ...

I cannot offer any statistics on how many IP addresses are on average reliable but what I can tell you that it is almost impossible to tell if a given IP address is the real clients address.

↙厌世 2024-07-20 12:58:39

IP +“用户代理”对于唯一访问者来说可能更好。

IP + "User Agent" could be a better for unique visitor.

稚气少女 2024-07-20 12:58:39

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

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

大多数代理服务器都支持 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、思科缓存引擎、McAfee Web Gateway、Phion
Airlock、Finjan's Vital Security、NetApp NetCache、jetNEXUS、Crescendo
Networks 的 Maestro、Web Adjuster 和 Websense Web 安全网关。

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

If you're behind a proxy, you should use X-Forwarded-For: http://en.wikipedia.org/wiki/X-Forwarded-For

It is an IETF draft standard with wide support:

The X-Forwarded-For field is supported by most proxy servers,
including Squid, Apache mod_proxy, Pound, HAProxy, Varnish cache,
IronPort Web Security Appliance, AVANU WebMux, ArrayNetworks,
Radware's AppDirector and Alteon ADC, ADC-VX, and 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 and Websense Web Security Gateway.

If not, here are a couple other common headers I've seen:

对岸观火 2024-07-20 12:58:39

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

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

Call the Below Action Method from your JS file (To get the ipv4 ip address).

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

Check after keeping Breakpoint, and use as per your requirement.
Its working fine for me.

岁月染过的梦 2024-07-20 12:58:38

除了 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
  • X_REAL_IPX -真实IPx-real-ip

In addition to REMOTE_ADDR and HTTP_X_FORWARDED_FOR there are some other headers that can be set such as:

  • HTTP_CLIENT_IP
  • HTTP_X_FORWARDED_FOR can be comma delimited list of IPs
  • HTTP_X_FORWARDED
  • HTTP_X_CLUSTER_CLIENT_IP
  • HTTP_FORWARDED_FOR
  • HTTP_FORWARDED
  • X_REAL_IP or X-Real-IP or x-real-ip
走走停停 2024-07-20 12:58:38

这取决于您网站的性质。

我碰巧开发了一些 IP 跟踪很重要的软件,在合作伙伴站点使用的字段中,我猜大约 20% - 40% 的请求要么是可检测到的欺骗 IP,要么是标头被清空,具体取决于请求的时间。日期和他们来自哪里。 对于获得有机流量(即不通过合作伙伴)的网站,我预计良好 IP 的比例要高得多。

正如 Kosi 所说,请小心您的行为 - IP 绝不是识别唯一访问者的可靠方法。

It depends on the nature of your site.

I happen to work on a bit of software where IP tracking is important, and within a field consumed by parter sites I'd guess some 20% - 40% of requests are either detectably spoofed IPs or headers blanked out, depending on the time of day and where they came from. For a site which gets organic traffic (i.e. not through partners) I'd expect a much higher ratio of good IPs.

As Kosi said, be careful what you're doing with this - IPs are in no way a reliable way to identify unique visitors.

意中人 2024-07-20 12:58:38

我已将 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)
    };
}

I've ported Grant Burton's PHP code to an ASP.Net static method callable against the HttpRequestBase. It will optionally skip through any private IP ranges.

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)
    };
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文