If I throw both of these two into a database, is that enough to prevent a site visitor from spoofing that they are coming from a different IP address?
$ip1 = $_SERVER['HTTP_X_FORWARDED_FOR'];
$ip2 = $_SERVER['REMOTE_ADDR'];
Short answer - no. You can never guarantee a link between an IP and a person. For almost all practical purposes though, yes, that's good enough.
Unless you're really expecting someone to go to significant lengths to hide their IP, you should be fine.
It really depends on what you're trying to do as to whether it's good enough or not.
Edit: Just seen your comment on the first post. An option for that is to look at Evercookies - they're extremely invasive and unethical, but they do do a very good job, so it's your call.
HTTP_X_FORWARDED_FOR is an HTTP header, so it can be easily spoofed. REMOTE_ADDR is an environment variable provided by the web server as specified in the CGI specification. It can't be easily spoofed. So there's no real point in caching either one.
If someone is spoofing the HTTP_X_FORWARDED_FOR header using a compromised proxy server, then there's not much you can do about that.
However, it should be easier to trust that a non-proxied request isn't spoofed since, unless the attacker is on the same subnet, they're limited to a blind spoofing attack, which requires them to guess the sequence and acknowledge numbers of the TCP connection. This is very difficult to do on modern networks. And even if they managed to do this, they wouldn't be able to receive any data routed to the spoofed IP, so it would be easy to filter out any blind spoofing attacks.
These days, it's not really possible (ok, nothing is impossible, but it does require very specific circumstances and has limited applications) for someone to actually use a spoofed IP to make an anonymous TCP connection to a web server. At most packet spoofing is used for DDoS and flood attacks.
Nope, it's very easy to spoof X-FORWARDED-FOR, which is literally sent as a header. Try making an account system or have it rely on email or something. You cannot trust IP alone.
Given your above comment, $_SERVER['REMOTE_ADDR'] will most likely suffice since they'd have to change IPs every time they wanted to perform that action, which would involve proxying or most likely Tor.
No, you may only avoid using some proxy servers.
There are still options that allows you to "hide" your real IP (and many people like they privacy), such as:
VPN
NAT
ssh and for example links
port forwarding
connection from work, school, bar, friend...
...
IP logging helps you just again some kind of users anyway (and many of them uses dynamic ips from their provides pools so without court order you will never know who's that and ban many people with him/her) but against admin with 150 machines available...
When any user visit your site with a proxy server in that case both value are different.You can get the IP address using HTTP_X_FORWARDED_FOR server variable.So in that case 'HTTP_X_FORWARDED_FOR' was posted by proxy server, which describe the client IP, while 'REMOTE_ADDR' describe the IP of proxy server.
When any user visit your site without any proxy server in that case you can get the IP address using REMOTE_ADDR server variable. So in that case HTTP_X_FORWARDED_FOR and HTTP_VIA will not be available.
Related
During a recent DDoS attack on a DNS, my site was unable to continue to function.
While the main site remained up and running, I was unable to connect to an external API on a different domain, leading the site to become completely unusable.
The data is fetched using PHP:
file_get_contents(API_PATH)
I currently call the API using the domain name, but I'm able to use the IP address if required.
Are there any advantages/disadvantages of calling the API by IP?
Is there anything else I should be aware of before making this change?
Your machine probably cached the resolved host, and has not updated it yet. You would need to flush the DNS cache.
It is not a good idea to use an IP address. While the DNS provider might be attacked, it is more likely that the IP is changed. I mean, rarely is a DNS provider attacked in such a large scale like the recent one, and it is definitely more common to see a website to have its IP address changed (although it shouldn't always happen normally). Therefore, you may want to use an IP address during a DNS attack (although your own DNS provider might be attacked as well), but not as the normal condition. If you want to be safe, fallback to use a cached IP address if the domain fails to resolve; but writing that kind of code is meaningless anyway, since it is rarely useful.
If you use an IP address:
You can't move the service to a different server without updating all the client code
You can't use some forms of load balancing
You can't use the hostname to distinguish between multiple services hosted on the same IP address
There really is not much difference in using the DNS name or IP address in your API calls.
However, If you ever change the service provider, you will have to update both your DNS settings (such as your A records) and the IP address in your code, you would not have to do this otherwise.
Other than that, pretty much good to go, unless someone else thinks of any other reason.
I assume you want to mitigate the impact of the recent DNS DDoS attacks.
Advantages:
Your website still functions. Although that may not matter too much because nobody will be able to even visit your website.
Disadvantages:
What if the API changes its IP address?
What if the API does load balancing using DNS (e.g. it may resolve to different IPs at different times)?
What if the API uses a CDN?
I do not recommend it.
I'm using multiple services to accept mobile payments for stuff like virtual currency.
Many companies will include an MD5 signature in the POST or GET callback which I can calculate to verify that the request is authentic and then reward the user with the purchased credits.
This method is very secure as it's nearly impossible to guess the signature.
Other companies will not provide a signature and just tell me to check if the call is from their server IP like the following code:
<?php
if(!in_array($_SERVER['REMOTE_ADDR'],array('xxx.xx.x.xx'))) {
header('HTTP/1.0 403 Forbidden');
die('Error: Unknown IP');
}
?>
Is this IP check secure enough?? Isn't it now very easy to spoof an IP address and make a GET or a POST request using that IP?
The other answers are incorrect. So I'll write my own.
With the exception of exceedingly rare situations, REMOTE_ADDR is 100% trust worthy. It comes from the TCP connection to the server, so it's practically impossible to forge without actually compromising something on the network (like the router the IP belongs to) or without having your server misconfigured (severely, Apache doesn't even let you misconfigure it like that).
So, there are two questions that I can see:
Is it safe to trust REMOTE_ADDR
Yes.
If the REMOTE_ADDR variable in PHP indicates the request came from their server, then it came from their server.
If you're using a remote proxy, then X-HTTP-FORWARDED-FOR is not to be trusted. That's where you can get into problems if you're not careful.
Is MD5() of the request safer than REMOTE_ADDR verification
NO!!!
It's a lot easier to forge an MD5 signature than it is to forge an IP address (which requires you to breach specific network hardware). And if the attacker breaches the network hardware, the game is over anyway.
What's The Best Solution?
The best solution is three fold:
Use HTTPS with Certificate Pinning
On your app, store the public key of their server. Then force the peer verification to use that certificate. That means that an attacker would need to steal the certificate of the remote server to be able to connect.
Verify IP Addresses
Using REMOTE_ADDR
Sign requests using HMAC+SHA2
Use HMAC with SHA-256 or SHA-512.
But yes, the IP check alone is quite secure.
To go deeper, we'd need to go into what types of attacks you're defending from.
Relying on server remote address is not a secure way since IP spoofing can breach the security.
But yes there are some ways by which you can prevent it like key exchange between the machines but still there is no assurance.
Better you should not rely on IP based security.
Is there a safe way, to identify a device which might be behind a Router (so the IP is not unique) in PHP?
Background: I have several embedded devices (self programmed & adaptable) which contact a webserver (php+mysql) with status updates. These updates are then - if the source is confirmed - saved to the database.
As I understand it $_SERVER['REMOTE_ADDR'] usually can be trusted (except some IIS configuration where it may - under special circumstances - wrongfully return 127.0.0.1; but different story)
Anyhow since I use SSL, the IP address really should not be a problem, because there a handshake is required and if the IP is faked or simply wrong, the connection should not be established
For now I require IP addresses to be whitelisted by admin, for an status update to be acceppted
The device additionally sends the MAC address via $_POST to identify the different modules with identical IP address (I know this can very easily be forged, and right now will be trusted if the IP address is trusted)
So first of all I am not sure if the IP address in itself is enough for it to be safe from attacks from the outside
Secondly if the device is behind a router, it will have the same IP address as every PC/device on that network. So about anyone there could forge a status update with a fake MAC address (simply as post variable), and since the IP address is whitelisted it will be trusted
So is there any way of confirming the identity of a device, or do you know a better way of doing this?
Aside: Going the other way, and have the webserver poll the different devices might be an option, but since there might be many (> 2000) devices of which we need the very last status (change) I thought it to be inefficient.
IP addresses can be spoofed, MAC addresses can be forged, so theses methods are not sufficient. The general approach is to assign a key to each client device (possibly the same key to all devices, even if this probably a bad idea). The "key" can be anything from a predefined string (weak, think username/password) to a signed certificate (strong, think SSL).
Both can be implemented either at the application level (by PHP) or at server level. If your application runs on Apache httpd server, I would rather recommend using its built-in features as it supports both approaches.
Is it possible to fake or hijack a content of $_SERVER['REMOTE_ADDR'] variable?
I would like to fake a request with:
$_SERVER['REMOTE_ADDR']='127.0.0.1';
How could I do that with PHP? Can CURL do that somehow?
I assume that you mean faking it remotely. The short answer is yes you can. The long answer about how easy it is depends on how you want to fake it.
If you don't care about receiving a response, it's as trivial as opening a raw socket to the destination and forging the source IP address. I'm not sure if it's really easy to do in PHP since all of PHP's socket implementations are at or above the TCP level. But I'm sure it's possible. Now, since you're not in control of the network, the response will not go back to you. So that means that you cannot (reliably anyway) create a TCP connection via a trivial forged TCP header (since the syn-ack does prevent this by requiring two-way communication).
However, if you can compromise the gateway the IP is off of, you can do whatever you'd like. So if you compromise the wifi router a computer is connected to, you can pretend to be that computer, and the server won't tell the difference. If you compromise the ISP's outbound router, you can (in theory at least) pretend to be the computer and the server won't tell the difference.
For some more info, see these following links:
ServerFault Question
Symantec Article
Linux Security Article
However, you will only be able to forge the 127.0.0.1 loopback address under TCP if you actually compromise the local machine/server. And at that point does it really matter?
Important
If you're using a framework to access this information, be absolutely sure that it does not check the X-HTTP-FORWARDED-FOR header! Otherwise it's trivial to fake the IP address. For example, if you're using Zend Framework's Zend_Controller_Request_Http::getClientIp method, be absolutely sure that you pass false as the parameter! Otherwise someone just needs to send an HTTP header: X-Http-Forwarded-For: 127.0.0.1 and they now appear to be local! This is one case where using a framework without understanding how it works in the backend can really be bad...
Edit: Relevant
I wrote a blog post recently about how I stumbled across a vulnerability in StackOverflow's application. It's very relevant here, since it exploits a very similar mechanism to what this question is looking for (although the circumstances around it are somewhat narrow):
How I Hacked StackOverflow
The remote address is not something added out of courtesy, it's used in the IP protocol to route packages, so if you send a package with a fake address, you will not receive a response, and since you're talking about a HTTP request, which is delivered over a TCP connection, which takes several IP packets (and the matching responses) to set up:
No, that's impossible (except of course by actually sending the request from the same host via the loopback interface).
Apache populates $_SERVER['REMOTE_ADDR'] from a TCP socket that it uses to communicate with your browser. It is IMPOSSIBLE to influence this variable over the open internet because of the three-way-handshake. If the client and the server is on a broadcast network, like wifi, then you can sniff the wire and complete the handshake.
If you browse via a proxy, $_SERVER['REMOTE_ADDR'] may be set to the proxy's IP address rather than the end user's.
There are other headers which you can use instead in this case: This page gives a function which checks all the possibilities and provides the address most likely to be the end user's:
http://roshanbh.com.np/2007/12/getting-real-ip-address-in-php.html
However if the user is proxying using a badly configured proxy, or a malicious one, or one designed to anonymise the end user, then you won't be able to guarantee any of the headers other than REMOTE_ADDR (which would only lead you as far as the proxy).
If your end user is browsing via HTTPS, then REMOTE_ADDR will always be his IP address; you can't use proxy forwarding via HTTPS. Therefore, the one way to be absolutely sure of his address is to get him to open your site in HTTPS.
You can overwrite any item in the $_SERVER array, including the one you mention, in your server; of course, not in someone else's.
However, it won't change your computer's IP address.
REMOTE_ADDR
The IP address from which the user is viewing the current page.
You can request script using proxy, etc. to change IP address but you cannot set there any text you want.
That is a variable set by apache or whatever server you're using. You cannot spoof it.
You may run $_SERVER['REMOTE_ADDR']='127.0.0.1'; at the beginning of the scripts, but i doubt thats what you're trying to do
Is it safe to trust $_SERVER['REMOTE_ADDR']? Can it be substituted by changing the header of request or something like that?
Is it safe to write something like that?
if ($_SERVER['REMOTE_ADDR'] == '222.222.222.222') { // my ip address
$grant_all_admin_rights = true;
}
Yes, it's safe. It is the source IP of the TCP connection and can't be substituted by changing an HTTP header.
One case you may want to be worry of is if you are behind a reverse proxy in which case the REMOTE_ADDR will always be the IP of the proxy server and the user IP will be provided in an HTTP header (such as X-Forwarded-For). But for the normal use case reading REMOTE_ADDR is fine.
$_SERVER['REMOTE_ADDR'] is the IP address the TCP connection came in on. While it is technically possible to bidirectionally spoof IP addresses on the Internet (by announcing foul routes via BGP), such attacks are likely to be spotted and not available to the typical attacker - basically, your attacker must have control over an ISP or carrier. There are no feasible unidirectional spoofing attacks against TCP (yet). Bidirectional IP spoofing is trivial on a LAN though.
Also be aware that it may be not be an IPv4, but an IPv6 address. Your current check is fine in that regard, but if you would check that 1.2.3.4 only occurs anywhere within $_SERVER['REMOTE_ADDR'], an attacker could simply connect from 2001:1234:5678::1.2.3.4.
Summarily, for anything other than critical (banking/military/potential damage >50.000€) applications, you can use the remote IP address if you can exclude attackers in your local network.
As mentioned above, it's not absolutely safe. But it doesn't mean, that you shouldn't use it. Consider combine this with some other methods of authentication, for instance checking COOKIE values.