Получение IP-адреса клиента: REMOTE ADDR, HTTP X FORWARDED FOR, что еще может быть полезно?
Я понимаю, что это стандартная практика, чтобы взглянуть на обе эти переменные. Конечно, их легко обмануть. Мне интересно, как часто вы можете ожидать эти значения (особенно HTTP_X_FORWARDED_FOR
), чтобы содержать подлинную информацию, а не просто скремблировать или лишить их значений?
кто-нибудь с опытом или статистикой по этому вопросу?
что-нибудь еще, что может быть полезно для задачи получения IP-адреса клиента?
6 ответов
Это зависит от характера вашего сайта.
Я работаю над немного программного обеспечения, где важно отслеживание IP, и в поле, потребляемом партерными сайтами, я бы предположил, что некоторые 20% - 40% запросов либо обнаруживают поддельные IPs, либо заголовки отключены, в зависимости от времени суток и откуда они пришли. Для сайта, который получает органический трафик (т. е. не через партнеров), я ожидал бы гораздо более высокого соотношения хороших IPs.
Как вы сказали, Будьте осторожны, что вы делать с этим-IPs никоим образом не являются надежным способом идентификации уникальных посетителей.
кроме REMOTE_ADDR
и HTTP_X_FORWARDED_FOR
есть некоторые другие заголовки, которые можно установить, например:
HTTP_CLIENT_IP
-
HTTP_X_FORWARDED_FOR
может быть разделен запятыми список IPs HTTP_X_FORWARDED
HTTP_X_CLUSTER_CLIENT_IP
HTTP_FORWARDED_FOR
HTTP_FORWARDED
я нашел код на следующем сайте полезным:
http://www.grantburton.com/?p=97
я портировал 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)
};
}
никакого реального ответа на ваш вопрос, но:
Обычно полагаться на IP-адрес клиентов, на мой взгляд, не является хорошей практикой, поскольку он не может использоваться для идентификации клиентов уникальным способом.
проблемы на дороге в том, что есть довольно много сценариев, где IP действительно не выравнивается с клиентом:
- Прокси / Webfilter (калечить почти все)
- анонимайзер сети (здесь тоже нет шансов)
- NAT (внутренний IP не очень полезно для вас)
- ...
Я не могу предложить никакой статистики о том, сколько IP-адресов в среднем надежный, но я могу сказать вам, что почти невозможно сказать, является ли данный IP-адрес реальным адресом клиентов.
Если вы находитесь за прокси, вы должны использовать X-Forwarded-For
: http://en.wikipedia.org/wiki/X-Forwarded-For
Это проект стандарта IETF С широкой поддержкой:
поле X-Forwarded-For поддерживается большинством прокси-серверов, в том числе Squid, Apache mod_proxy, фунт, HAProxy, лак кэш, IronPort Web Security Appliance, AVANU WebMux, ArrayNetworks, AppDirector от Radware Alteon и АЦП, АЦП-ЧХ, и АЦП-ва клавишу F5 большой IP, Blue Coat ProxySG, Cisco Cache Engine, Веб-Шлюз McAfee, Phion Шлюз, жизненно важная безопасность Финджана, NetApp NetCache, jetNEXUS, Crescendo Networks ' Maestro, Web Adjuster и Websense шлюз безопасности.
если нет, вот несколько других общих заголовков, которые я видел: