Question:
I'm having a problem using the: $_SERVER["REMOTE_ADDR"];
eventually it takes an IP that is not the user's, in this case it started after I started using CloudFlare …
Does anyone know why this happens? Is there a better way to get the user's IP ?
Answer:
Does anyone know why this happens?
This is because CloudFlare acts as a proxy, the user connects to a nearby CloudFlare server and then CloudFlare connects to your server if necessary as some content may be cached. If it's not cached CloudFlare will get a response from the server and deliver it to the user.
Basically:
User (1.2.3.4) -> CloudFlare (5.6.7.8) -> Server (9.1.2.3)
Assuming these are IPs, you will get 5.6.7.8 by the common method.
Is there a better way to get the user's IP?
Just because it serves as a proxy it includes the X-Forwarded-For
header, but that's not so secure, but it also includes the CF-Connecting-IP
.
The X-Forwarded-For
header:
Provided there is no such header defined, CloudFlare will create:
X-Forwarded-For: 1.2.3.4
Therefore, 1.2.3.4 is the client's IP.
On condition that one already exists, CloudFlare will add:
X-Forwarded-For: 1.2.3.4, AAAA, XXXX
In theory the first would be the client's IP, as X-Forwarded-For
is defined by proxy, but in practice the situation is different, because the user can spoof X-Forwarded-For
, in this way the last one would be the real one.
The CF-Connecting-IP
header:
CloudFlare creates this header, this will offer the last IP, this is usually the same value as X-Forwarded-For
, in "normal" situation.
quick fix
PHP:
$_SERVER["REMOTE_ADDR"] = $_SERVER["HTTP_CF_CONNECTING_IP"];
This will get the informed header containing the user's IP and will replace it in the $_SERVER["REMOTE_ADDR"]
variable, this can reduce the necessary changes. 😉
However, this creates a problem, because if the traffic doesn't go through CloudFlare the user can spoof the IP by sending such a header.
Solution
PHP:
$_SERVER["REMOTE_ADDR"] = $_SERVER["HTTP_CF_CONNECTING_IP"];
NGINX:
server {
listen 80;
# ...
# Impede acesso fora do CloudFlare:
allow 103.21.244.0/22;
allow 103.22.200.0/22;
allow 103.31.4.0/22;
allow 104.16.0.0/12;
allow 108.162.192.0/18;
allow 131.0.72.0/22;
allow 141.101.64.0/18;
allow 162.158.0.0/15;
allow 172.64.0.0/13;
allow 173.245.48.0/20;
allow 188.114.96.0/20;
allow 190.93.240.0/20;
allow 197.234.240.0/22;
allow 198.41.128.0/17;
allow 199.27.128.0/21;
allow 2400:cb00::/32;
allow 2405:8100::/32;
allow 2405:b500::/32;
allow 2606:4700::/32;
allow 2803:f800::/32;
allow 2c0f:f248::/32;
allow 2a06:98c0::/29;
deny all;
#####################################
# Define para salvar todos os logs com o IP do cliente:
set_real_ip_from 103.21.244.0/22;
set_real_ip_from 103.22.200.0/22;
set_real_ip_from 103.31.4.0/22;
set_real_ip_from 104.16.0.0/12;
set_real_ip_from 108.162.192.0/18;
set_real_ip_from 131.0.72.0/22;
set_real_ip_from 141.101.64.0/18;
set_real_ip_from 162.158.0.0/15;
set_real_ip_from 172.64.0.0/13;
set_real_ip_from 173.245.48.0/20;
set_real_ip_from 188.114.96.0/20;
set_real_ip_from 190.93.240.0/20;
set_real_ip_from 197.234.240.0/22;
set_real_ip_from 198.41.128.0/17;
set_real_ip_from 199.27.128.0/21;
set_real_ip_from 2400:cb00::/32;
set_real_ip_from 2405:8100::/32;
set_real_ip_from 2405:b500::/32;
set_real_ip_from 2606:4700::/32;
set_real_ip_from 2803:f800::/32;
set_real_ip_from 2c0f:f248::/32;
set_real_ip_from 2a06:98c0::/29;
real_ip_header CF-Connecting-IP;
#####################################
# ...
}
If you are using Apache (or any other) look for equivalent functions.
This will ensure that it is only possible to access the website if you pass through CloudFlare, which will necessarily contain the HTTP_CF_CONNECTING_IP
, furthermore NGINX will save all logs using the client's IP and no longer the proxy. 😉