Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get client's real IP address on Heroku

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?

like image 240
caw Avatar asked Aug 16 '13 01:08

caw


People also ask

Does Heroku have an IP address?

Heroku does not provide Static IP addresses in the Common Runtime Environment. In Heroku Private Spaces, which costs thousands of dollars a month, Static IP's are available, but they aren't load balanced, run on dedicated proxies, or highly customizable.

How do I get a static IP on Heroku app?

The Inbound Proxy allows you to always access your Heroku app on a Static IP address. This feature is available on our Micro plan and above. Once you have provisioned the add-on you just need to visit the QuotaGuard Static Dashboard to complete your setup.

Is Heroku IP dynamic?

We offer three products on Heroku, QuotaGuard, QuotaGuard Static, and QuotaGuard Shield. QuotaGuard routes your traffic through a dynamic set of IP addresses that may change at any time and is intended for accessing APIs like Google Maps that restrict usage based on your IP address.


2 Answers

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'];     } } 
like image 128
caw Avatar answered Sep 18 '22 15:09

caw


I work in Heroku's support department and have spent some time discussing this with our routing engineers. I wanted to post some additional information to clarify some things about what's going on here.

The example provided in the answer above just had the client IP displayed last coincidentally and that's not really guaranteed. The reason it wasn't first is because the originating request claimed that it was forwarding for the IP specified in the X-Forwarded-For header. When the Heroku router received the request, it just appended the IP that was directly connecting to the X-Forwarded-For list after the one that had been injected into the request. Our router always adds the IP that connected to the AWS ELB in front of our platform as the last IP in the list. This IP could be the original one (and in the case where there's only one IP, it almost certainly is), but the instant there are multiple IPs chained, all bets are off. Convention is always to add the latest IP in the chain to the end of the list (which is what we do), but at any point along the chain that chain can be altered and different IPs could be inserted. As such, the only IP that's reliable (from the perspective of our platform) is the last IP in the list.

To illustrate, let's say someone initiates a request and arbitrarily adds 3 additional IPs to the X-Forwarded-For header:

curl -H "X-Forwarded-For: 12.12.12.12,15.15.15.15,4.4.4.4" http://www.google.com 

Imagine this machine's IP was 9.9.9.9 and that it had to pass through a proxy (e.g., a university's campus-wide proxy). Let's say that proxy had an IP of 2.2.2.2. Assuming it wasn't configured to strip X-Forwarded-For headers (which it likely wouldn't be), it would just tack the 9.9.9.9 IP to the end of the list and pass the request on to Google. At this point, the header would look like this:

X-Forwarded-For: 12.12.12.12,15.15.15.15,4.4.4.4,9.9.9.9 

That request will then pass through Google's endpoint, which will append the university proxy's IP of 2.2.2.2, so the header will finally look like this in Google's logs:

X-Forwarded-For: 12.12.12.12,15.15.15.15,4.4.4.4,9.9.9.9,2.2.2.2 

So, which is the client IP? It's impossible to say from Google's standpoint. In reality, the client IP is 9.9.9.9. The last IP listed is 2.2.2.2 though and the first is 12.12.12.12. All Google would know is that the 2.2.2.2 IP is definitely correct because that was the IP that actually connected to their service – but they wouldn't know if that was the initial client for the request or not from the data available. In the same way, when there's just one IP in this header – that is the IP that directly connected to our service, so we know it's reliable.

From a practical standpoint, this IP will likely be reliable most of the time (because most people won't be bothering to spoof their IP). Unfortunately, it's impossible to prevent this sort of spoofing and by the time a request gets to the Heroku router, it's impossible for us to tell if IPs in an X-Forwarded-For chain have been tampered with or not.

All reliability issues aside, these IP chains should always be read from left-to-right. The client IP should always be the left-most IP.

like image 41
Joel Watson Avatar answered Sep 20 '22 15:09

Joel Watson