Get actual user ip

Get actual user ip

You should get info from X-Forwarded-For

When using X-Forwarded-For, the assumption normally made is that the first IP address in the list is the client address. But what about private addresses? What about the casual browser at  a company network with a 192.168.x.x internal address structure? You’ll end up with a very unhelpful address that’ll tell you nothing .

There are 3 sets of address ranges in IPv4 (lets ignore IPv6 for now) that are reserved for private networks. Normally these are hidden behind NAT gateways and often traffic is forced to either manually or automatically route through a proxy server of some kind. The address ranges are:

  • 10.0.0.0 – 10.255.255.255
  • 172.16.0.0 – 172.31.255.255
  • 192.168.0.0 – 192.168.255.255

You can thank these beauties for extending the life of IPv4 way beyond what it would otherwise have been.

If you have a client behind one of these networks and it’s not routed through a proxy server then you’ll probably just get the IP address of the NAT gateway which is likely to be the address you want to use. If the request is routed through a proxy server then you may get an X-Forwarded-For that looks something like this:

X-Forwarded-For: 10.208.4.38, 58.163.175.187

Where the address you probably want is actually the (proxy) server address on the end rather than the private client address.

You may also have a chain of multiple servers, perhaps you have a downstream proxy server going through a larger upstream one before heading out of the network, so you may get something like this:

X-Forwarded-For: 10.208.4.38, 58.163.1.4, 58.163.175.187

Or, the downstream proxy server could be within the private network, perhaps a departmental proxy server connecting to a company-wide proxy server and then this may happen:

X-Forwarded-For: 10.208.4.38, 10.10.300.23, 58.163.175.187

This could of course be even more complex as you may have a longer chain of proxy servers (although I’ve never actually seen anyone chain more than 2 layers of proxy servers together in a network before).

So what general rule should we construct for extracting our usable client IP from these addresses?

Of course, I’m suggesting that the rule: always use the leftmost address is not correct as there is a good chance it may be a private IP address if there is more than 1 address in the list. Unfortunately this is the rule that mod_geoip adopts, if it finds a comma it just chops off the string at that comma. We immediately found this led to unsatisfactory results with ELB as we had more requests than we expected originating from private networks routed through proxy servers; and we heard about it in the form of error reports from our users.

An alternative would be always use the rightmost address which would probably get you a pretty good guess in almost all cases. If there is more than one IP address in the list then the rightmost address will probably be the address where the request left whatever corporate or internal network the client was hidden behind, even if there are multiple layers. However, multiple layers of IP addresses suggests a fairly large network, possibly widely disbursed. There’s also a chance that you have one proxy server piggybacking off a higher capacity upstream proxy server: for example, some ISPs run their own very large proxy servers that customers can use and may make ideal upstream connections for internal proxy servers with caching at both levels. The ISP proxy server is likely to be located in a very different place to the client though and if you’re trying to pin down the IP address of the client using something like GeoIP City then you’ll probably get the wrong city.

So, here’s the rule that I suggest would be the best general case rule to allow you to extract the address most likely to be physically close to the real client:

Always use the leftmost non-private address.

We can do this because the rules are clear about what is and what is not a private IP address (see above).

Code to get ip in C# using X-Forwarded-For :

public bool CheckIP(string adress)
{
var rangeList = new List();
rangeList.Add(new IpRange(IPAddress.Parse(“192.168.0.0”), IPAddress.Parse(“192.168.255.255”)));
rangeList.Add(new IpRange(IPAddress.Parse(“10.0.0.0”), IPAddress.Parse(“10.255.255.255”)));
rangeList.Add(new IpRange(IPAddress.Parse(“172.16.0.0”), IPAddress.Parse(“172.31.255.255”)));
foreach (var range in rangeList)
{
List adressInt = adress.Split(‘.’).Select(str => int.Parse(str)).ToList();
List lowerInt = range.LowerIP.ToString().Split(‘.’).Select(str => int.Parse(str)).ToList();
List upperInt = range.UpperIP.ToString().Split(‘.’).Select(str => int.Parse(str)).ToList();

if (adressInt[0] >= lowerInt[0] && adressInt[0] < upperInt[0]) { return true; } else if (adressInt[0] >= lowerInt[0] && adressInt[0] == upperInt[0])
{
if (adressInt[1] >= lowerInt[1] && adressInt[1] < upperInt[1]) { return true; } else if (adressInt[1] >= lowerInt[1] && adressInt[1] == upperInt[1])
{
if (adressInt[2] >= lowerInt[2] && adressInt[2] < upperInt[2]) { return true; } else if (adressInt[2] >= lowerInt[2] && adressInt[2] == upperInt[2])
{
if (adressInt[3] >= lowerInt[3] && adressInt[3] <= upperInt[3])
{
return true;
}
}

}

}
}
return false;
}
public string GetIP()
{
string ipAddress;
var request = HttpContext.Current.Request;

if(request.ServerVariables.AllKeys.Contains(“HTTP_X_FORWARDED_FOR”))
{
foreach (string ip in request.ServerVariables[“HTTP_X_FORWARDED_FOR”].Split(‘,’))
{
if (!CheckIP(ip.Trim()))
{
ipAddress = ip.Trim();
return ipAddress;
}
}
}
if (request.ServerVariables.AllKeys.Contains(“HTTP_CLIENT_IP”) && !CheckIP(request.ServerVariables[“HTTP_CLIENT_IP”]))
{
ipAddress = request.ServerVariables[“HTTP_CLIENT_IP”];
return ipAddress;
}
if(request.ServerVariables.AllKeys.Contains(“HTTP_X_FORWARDED”) && !CheckIP(request.ServerVariables[“HTTP_X_FORWARDED”]))
{
ipAddress = request.ServerVariables[“HTTP_X_FORWARDED”];
return ipAddress;
}
if(request.ServerVariables.AllKeys.Contains(“HTTP_X_CLUSTER_CLIENT_IP”) && !CheckIP(request.ServerVariables[“HTTP_X_CLUSTER_CLIENT_IP”]))
{
ipAddress = request.ServerVariables[“HTTP_X_CLUSTER_CLIENT_IP”];
return ipAddress;
}
if(request.ServerVariables.AllKeys.Contains(“HTTP_FORWARDED_FOR”) && !CheckIP(request.ServerVariables[“HTTP_FORWARDED_FOR”]))
{
ipAddress = request.ServerVariables[“HTTP_FORWARDED_FOR”];
return ipAddress;
}
if(request.ServerVariables.AllKeys.Contains(“HTTP_FORWARDED”) && !CheckIP(request.ServerVariables[“HTTP_FORWARDED”]))
{
ipAddress = request.ServerVariables[“HTTP_FORWARDED”];
return ipAddress;
}
ipAddress = request.ServerVariables[“REMOTE_ADDR”];
return ipAddress;
}