Get client's real IP address on Heroku
heroku, http, ip-address
Solution
From Jacob, Heroku's Director of Security at the time:
The router doesn't overwrite `X-Forwarded-For`, but it does guarantee that the real origin will always be the last item in the list.
This means that, if you access a Heroku app in the normal way, you will just see your IP address in the `X-Forwarded-For` header:
$ curl http://httpbin.org/ip
{
"origin": "123.124.125.126",
}
If you try to spoof the IP, your alleged origin is reflected, but - critically - so is your real IP. Obviously, this is all we need, so there's a clear and secure solution for getting the client's IP address on Heroku:
$ curl -H"X-Forwarded-For: 8.8.8.8" http://httpbin.org/ip
{
"origin": "8.8.8.8, 123.124.125.126"
}
This is just the opposite of what is described on Wikipedia, by the way.
PHP implementation:
function getIpAddress() {
if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$ipAddresses = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
return trim(end($ipAddresses));
}
else {
return $_SERVER['REMOTE_ADDR'];
}
}
Problem
On any Heroku stack, I want to get the client's IP. my first attempt might be: ``` request.headers['REMOTE_ADDR'] ``` This does not work, of course, because all requests are passed through proxies. So the alternative was to use: ``` request.headers['X-Forwarded-For'] ``` But this is not quite safe, is it? If it contains only one value, I take this. If it contains more than one value (comma-separated), I could take the first one. But what if someone manipulates this value? I cannot trust `request.headers['X-Forwarded-For']` as I could with `request.headers['REMOTE_ADDR']`. And there is no list of trusted proxies that I could use, either. But there must be some way to reliably get the client's IP address, always. Do you know one? In their docs, Heroku describes that `X-Forwarded-For` is "the originating IP address of the client connecting to the Heroku router". This sounds as if Heroku could be overwriting the `X-Forwarded-For` with the originating remote IP. This would prevent spoofing, right? Can someone verify this?