PHP catching CloudFlare IP instead of user IP

5

I'm having a problem using the: $_SERVER["REMOTE_ADDR"]; eventually it takes an IP that is not the user's, in 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 ?

    
asked by anonymous 12.01.2017 / 03:25

2 answers

5

Does anyone know why this happens?

This happens because CloudFlare acts as a proxy, the user connects to a nearest CloudFlare server, and then CloudFlare connects to your server, if necessary, as some content may be cached. If it is not cached CloudFlare will get a response from the server and will deliver it to the user.

Basically:

User (1.2.3.4) - > CloudFlare (5.6.7.8) - > Server (9.1.2.3)

Assuming this to be 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 header X-Forwarded-For , but this is not as secure, but it also includes CF-Connecting-IP .

The header X-Forwarded-For :

If this heading does not exist, CloudFlare will create:

X-Forwarded-For: 1.2.3.4

So, 1.2.3.4 is the client IP.

If there is already a defined one, CloudFlare will add:

X-Forwarded-For: 1.2.3.4, A.A.A.A, X.X.X.X

In theory, the first would be the client IP, since X-Forwarded-For is defined by proxy, but in practice the situation is different because the user can falsify X-Forwarded-For , so the latter would be the true one.

The header CF-Connecting-IP :

CloudFlare creates this header, this will provide the last IP, this is usually the same as X-Forwarded-For , in a "normal" situation.

Quick solution

PHP:

$_SERVER["REMOTE_ADDR"] = $_SERVER["HTTP_CF_CONNECTING_IP"];

This will get the informed header containing the user's IP and will replace in the $_SERVER["REMOTE_ADDR"] variable, this can reduce the necessary changes. ;)

However, this creates a problem because if the traffic does not pass through CloudFlare the user can falsify 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 you can only access the website if it passes through CloudFlare, which will contain HTTP_CF_CONNECTING_IP , in addition NGINX will save all the logs using client IP and not more of the proxy. ;)

    
12.01.2017 / 06:10
3

Try this:

$http_client_ip       = $_SERVER['HTTP_CLIENT_IP'];
$http_x_forwarded_for = $_SERVER['HTTP_X_FORWARDED_FOR'];
$remote_addr          = $_SERVER['REMOTE_ADDR'];

/* VERIFICO SE O IP REALMENTE EXISTE NA INTERNET */
if(!empty($http_client_ip)){
    $ip = $http_client_ip;
    /* VERIFICO SE O ACESSO PARTIU DE UM SERVIDOR PROXY */
} elseif(!empty($http_x_forwarded_for)){
    $ip = $http_x_forwarded_for;
} else {
    /* CASO EU NÃO ENCONTRE NAS DUAS OUTRAS MANEIRAS, RECUPERO DA FORMA TRADICIONAL */
    $ip = $remote_addr;
}

I hope this helps you!

    
12.01.2017 / 03:34