Is the web server variable $_SERVER['REMOTE_ADDR'] reliable?
Asked Answered
J

2

11

I have generally assumed that in a PHP script I can test $_SERVER['REMOTE_ADDR'] to establish the IP address from which the request originated. However, I am starting to wonder if things are not a bit more complicated. Here is the scenario

  • I run a number of servers, call them A, B and C - on which users have to be "registered"
  • I run a separate registration server, call it S, where the users' credentials etc are first verified prior to sending out a complete registration request to servers A, B and C

The request goes out as

file_get_contents('https://url?data=value')

On servers A, B and C I was quite naively testing $_SERVER['REMOTE_ADDR'] to establish that the request was in fact coming from server S. Much to my surprise the results turned out to be patchy and variable

  1. The value in REMOTE_ADDR was the IP address of the human user interacting with the registration server, S
  2. The value in REMOTE_ADDR was the IP address of the registration server, S - what I had expected to see all the time
  3. The value in REMOTE_ADDR was another IP address from the pool of IP addresses on the virtual server where I host server S

I don't really need to perform this additional verification test so I can drop it out altogether. Nevertheless this result has taken me by surprise so I am curious to see if someone here can shed some light on what is going on.

I should mention that I am running PHP 5.5 on Lighttpd on servers A, B and C and PHP 5.3 on Apache 2 on server S.

Jeffreyjeffreys answered 24/6, 2014 at 14:5 Comment(12)
Do you use PHP as a module or fcgi/fpm? Please check also the variables $_SERVER['HTTP_CLIENT_IP'] and $_SERVER['HTTP_X_FORWARDED_FOR'], as all of the three variables are differently populated when using different servers, (reverse)proxies and configuration.Friedcake
Duplicates: 1, 2. What is your exact question? If it is "is REMOTE_ADDR unreliable" the answer is "yes, it is, if you expect it to contain always some specific computers address. It is reliable in the way it tells an ip of some computer that the connection is coming from".Wristlet
the ip address $_SERVER['REMOTE_ADDR'] is the public IP address to directly respond to - it's not possible to fake it (unless the attacker doesn't care to receive the response). From what's in the question: there are flaws in how you're testing, as that's not how it works.Raincoat
@Wristlet That's a pretty misleading comment.Raincoat
If you are doing server to server communication (Server S to Server A), it is impossible to have the human visitor's IP in REMOTE_ADDR.Friedcake
@Wristlet because the remote addr is unambiguous. If it were unreliable a server would not be able to send a response to a client (browser). The first duplicate indicates some kind of misconfiguration, it's not the norm; the second clearly states It is the source IP of the TCP connection. Basically if it was unreliable the web as a whole wouldn't work.Raincoat
@Raincoat REMOTE_ADDR can point to a firewall, a proxy, a VPN endpoint or something else. For this computer I'm using I can select whichever proxy I want, so it is not reliable in telling what computer I'm using. That has nothing to do with server ability to send out a response to a client.Wristlet
@Wristlet please read the question again - is any of that relevant? Is it possible for me to make a request to server A, server A to issue file_get_contents('http://google.com'); and google to see my public IP (or that of my router, or that of my proxy, or that of my firewall etc.)?Raincoat
@Raincoat Sure it is. Server-to-server traffic can, and often do, use proxies too, especially in an enterprise context. They can also use gateway servers, for instance.Wristlet
@Wristlet you think that DroidOS has configured his server to use the client as a proxy for outbound requests? Are we reading different questions? If you have a proof of concept that calls file_get_contents('http://example.com'); passing the end-clients IP I'd like to see it.Raincoat
@Raincoat I did not claim that DroidOS has configured proxies, and I did not mention it passing the end-user ip. However, I do think your statement "the remote addr is unambiguous. If it were unreliable a server would not be able to send a response to a client (browser)." is the misleading comment here.Wristlet
I believe REMOTE_ADDR can also contain a list of IP numbers, or even "unknown".Shenitashenk
R
3

REMOTE_ADDR is a variable that Apache (or any other web container) fills, it contains the IP address of the terminal at the other end of the communication.

Is it reliable? Yes.

Is it secure? Depends, if you use it thinking that it presents you with the IP address of the user making the call, you're wrong, any proxy standing in the way will corrupt the information.

In your case, the server emitting the HTTP call should provide its IP address, so scenario 2 should happen all the time. I don't know what went wrong at what moment but its weird.

To respond to Dany Caissy, don't rely on HTTP_X_FORWARDED_FOR, it can easily be modified as it's an HTTP header, and not a TCP/IP property.

Romanov answered 24/6, 2014 at 14:16 Comment(3)
I'm just giving him the options, I didn't claim any of them were safe or not, it's his responsibility to account for security and it isn't part of this question.Inapt
I know your intention was for a good reason :), it's just worth to mention for people coming to read this for the first time, that doing $myCustomersIP = $_SERVER["HTTP_X_FORWARDED_FOR"]; is wrong. But I totally agree with you :)Romanov
@Romanov getallheaders()['X-Forwarded-For'] ended up being the ideal solution to a head-scratcher. Thank you!Postmortem
I
2

REMOTE_ADDR isn't the only way to get the IP Address, there are also :

HTTP_CLIENT_IP
HTTP_X_FORWARDED_FOR
HTTP_X_FORWARDED
HTTP_X_CLUSTER_CLIENT_IP
HTTP_FORWARDED_FOR
HTTP_FORWARDED

They are set in different ways and can mean different things, ultimately, it is very difficult to get the IP Address you want to have.

EDIT : The only one of them that is reliable and can't be modified by the user is REMOTE_ADDR, but it won't always do exactly what you want, so you'll HAVE to use the other ones, no matter how 'unsafe' everyone says they are.

Inapt answered 24/6, 2014 at 14:11 Comment(8)
By all the gods - no. Don't even think to rely on X_FORWARDED_FORBohon
@AlmaDo X_FORWARDED_FOR can be relied on in some cases. Specifically when you have a cache/load-balancer in front of your server and always clear any external x-forwarded-for headers. Only the user-provided values should never be trusted.Hydrolyze
It can be checked (at best - to get some information). But it should never be reliedBohon
relying on X_FORWADED_FOR is a gaping security hole. See for example this post. it is very difficult to get the IP Address you want to have. - no it isn't =). The question is asking for the ip address of the machine initiating the request, not e.g. the public IP of the end-client.Raincoat
There is nothing else to rely on. If you run your own front-end proxies for anything, you have to rely on x-forwarded-for. Or some other header that will have exactly the same function. The only thing you could do to improve the trust is to sign the field, but I don't know of any software capable of that out of the box.Hydrolyze
@Hydrolyze there is "read" and there is "rely". Don't attribute any meaning (except possibly unique-but-anonymous identity) to headers which are user-defined - see the post I linked to earlier for an example.Raincoat
Thank you to everyone for the various comments. As I mention in my question I do not really need the additional IP address check. The data I send out are heavily encrypted and contain a stringent timeout that will, as a last resort, ensure that servers A,B, C ignore the request. So I have just dropped any notion of IP testing. It is nevertheless useful to cast away my ignorance and not place blind faith in REMOTE_ADDR. Lighttpd uses PHP FastCGI & I find that X_FORWARDED_FOR + CLIENT_IP are both undefined anyway.Jeffreyjeffreys
@Jeffreyjeffreys they are undefined because you're not using a proxy. If you want to restrict access by IP it's more appropriate to use ip-tables - but that's just the equivalent of testing REMOTE_ADDR which you can trust to mean the machine that the request will respond to.Raincoat

© 2022 - 2024 — McMap. All rights reserved.