Получение IP-адреса клиента: REMOTE_ADDR, HTTP_X_FORWARDED_FOR, что еще может быть полезным? - PullRequest
57 голосов
/ 09 февраля 2009

Я понимаю, что это стандартная практика, чтобы смотреть на обе эти переменные. Конечно, они могут быть легко подделаны. Мне любопытно, как часто вы можете ожидать, что эти значения (особенно HTTP_X_FORWARDED_FOR) будут содержать подлинную информацию, а не будут просто зашифрованы или их значения будут удалены?

Кто-нибудь с опытом или статистикой по этому материалу?

Есть ли что-нибудь еще, что может быть полезно для получения IP-адреса клиента?

Ответы [ 7 ]

60 голосов
/ 27 мая 2009

В дополнение к REMOTE_ADDR и HTTP_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

28 голосов
/ 09 февраля 2009

Зависит от характера вашего сайта.

Мне довелось работать над небольшим количеством программного обеспечения, в котором важно отслеживание IP-адресов, и в пределах поля, занимаемого сайтами-партнерами, я бы предположил, что около 20% - 40% запросов являются либо обнаруживаемыми поддельными IP-адресами, либо заголовки не отображаются, в зависимости от время суток и откуда они пришли. Для сайта, который получает органический трафик (то есть не через партнеров), я бы ожидал гораздо более высокого соотношения хороших IP-адресов.

Как сказал Коси, будьте осторожны с тем, что вы делаете с этим - IP-адреса никоим образом не являются надежным способом идентификации уникальных посетителей.

10 голосов
/ 27 марта 2012

Я перенес PHP-код Гранта Бертона в статический метод ASP.Net, вызываемый из HttpRequestBase. При желании он пропускает любые частные 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)
    };
}
7 голосов
/ 09 февраля 2009

Нет реального ответа на ваш вопрос, но:
Вообще полагаться на IP-адрес клиента, на мой взгляд, не очень хорошая практика, так как невозможно идентифицировать клиентов уникальным способом.

Проблемы на дороге заключаются в том, что существует довольно много сценариев, когда IP-адрес не совсем соответствует клиенту:

  • Прокси / Webfilter (искажать почти все)
  • Сеть анонимайзеров (здесь тоже нет шансов)
  • NAT (внутренний IP не очень полезен для вас)
  • ...

Я не могу предложить какую-либо статистику о том, сколько IP-адресов в среднем надежно , но я могу сказать вам, что почти невозможно определить, является ли данный IP-адрес реальным адресом клиента.

2 голосов
/ 09 февраля 2009

IP + "User Agent" может быть лучше для уникального посетителя.

1 голос
/ 01 апреля 2014

Если вы используете прокси-сервер, вы должны использовать X-Forwarded-For: http://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, AppDirector от Radware и АЦП Alteon, ADC-VX и ADC-VA, F5 Big-IP, Blue Coat ProxySG, Cisco Cache Engine, McAfee Web Gateway, Phion Airlock, жизненная безопасность Finjan, NetApp NetCache, jetNEXUS, Crescendo Maestro, Web Adjuster и Websense Web Security Gateway.

Если нет, вот пара других распространенных заголовков, которые я видел:

0 голосов
/ 22 мая 2019

Вызовите метод ниже действия из вашего файла JS (чтобы получить IP-адрес IPv4).

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

Проверьте после сохранения точки останова и используйте в соответствии с вашими требованиями. Это нормально работает для меня.

...