Categories
ASP.NET Software Engineering

Gotcha: HTTP_X_FORWARDED_FOR returns multiple IP addresses

I hit a small gotcha this evening. A visitor to Developer Fusion reported that they couldn’t gain access to the site at all, because our IP address detection logic was failing. We were checking the “HTTP_X_FORWARDED_FOR” header for an IP address, before falling back to REMOTE_ADDR, turning the IP into a long integer, and doing an IP-to-country lookup in our database. Which seemed safe enough!


As it turns out, HTTP_X_FORWARDED_FOR can sometimes have a comma delimited list of IP addresses – so what we actually needed to be doing was take the last IP address in that list, before doing our conversion to an integer.


Thanks go out to Francois Botha, one of our visitors, for helping me track down this issue!

18 replies on “Gotcha: HTTP_X_FORWARDED_FOR returns multiple IP addresses”

Damn. I”ve written apps using this property and never knew it could send more than one IP. Thanks for the heads-up 🙂

In which order is the comma delimited list returned?. For example, if I am trying to get “the” original ip, is it first in the list (always)?

i ran into this about a year ago, and wrote this snippet as a workaround. Only tested it with a few proxies (Google translate, etc.), so no guarantees…

if (!empty($_SERVER[”HTTP_X_FORWARDED_FOR”]))
$ip = $_SERVER[”HTTP_X_FORWARDED_FOR”];
else
$ip = $_SERVER[”REMOTE_ADDR”];

// When viewed through an anonymous proxy, the address string
// contans multiple ip#s separated hy commas. This fixes that.
$ip_array = explode(“,”, $ip);
$ip = $ip_array[0];

:neokio

instead of using first member from $_SERVER[”HTTP_X_FORWARDED_FOR”] retuning comma delimited value.

shouldn”t we be using the last member of the array ?

something like,

$ip = $ip_array[ COUNT($ip_array) as Computed – 1 ];

???

Thanks Viral and Neokio. The added line from Viral looks good and works for me. I was looking for a function like explode… so this is perfect.

substr($_SERVER[”HTTP_X_FORWARDED_FOR”], 0, 15);

if the first ip is the goal then this should suffice, no need for explode. But I am not at all clear if the goal the first IP or the last.

This doesn’t work for me. We have an incoming range of 210.149.133.31. So this makes it 14. Another incoming was 85.148.217.59, this make it 13.

I don’t think you can manage this by Exploding on the dots or comma.

I”m just taking care of this for myself…

if (!isset($_SERVER[”REMOTE_ADDR”]) && isset($_SERVER[”HTTP_X_FORWARDED_FOR”]))
{
$IP = array_pop(explode(”,”,$_SERVER[”HTTP_X_FORWARDED_FOR”]));
}

I think that should do it.

substr($_SERVER[”HTTP_X_FORWARDED_FOR”], 0, 15); Ip addresses could be smaller then 15 chars for example AA.BB.CC.DD is less then 15 chars and still valid. An alternative to explode would be strpos to find the position of the coma and substr from 0 to coma

$ip = $_SERVER[”HTTP_X_FORWARDED_FOR”];

if (($pos = strrpos($ip, ”,”)) !== false) {

  $ip = substr($ip, $pos+1);

}

re: tempest on 09 August, 2008

While an IPs maximum length is 15, it won”t always be the case…

substr($_SERVER[”HTTP_X_FORWARDED_FOR”], 0, strpos($_SERVER[”HTTP_X_FORWARDED_FOR”], ”,”));

The code samples here seem to prefer the http_x_forwarded_for over the remote_addr, this is strange since it actually easily can be forged, all you need is a proxy that changes that field.

For logging log both, for location use better features (html5) since ip is somewhat useless for that.

function getIp() {
$ip = $_SERVER[‘REMOTE_ADDR’];

if (!empty($_SERVER[‘HTTP_CLIENT_IP’])) {
$ip = $_SERVER[‘HTTP_CLIENT_IP’];
} elseif (!empty($_SERVER[‘HTTP_X_FORWARDED_FOR’])) {
$ips = $_SERVER[‘HTTP_X_FORWARDED_FOR’];
$ipa = explode(“,”, $ips);
$ip = $ipa[0];
}

return $ip;
}

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.