PHP picking up Cloudflare IP instead of user IP

Asked

Viewed 1,777 times

7

I’m having a problem using the: $_SERVER["REMOTE_ADDR"]; eventually he catches a IP which is not the user’s, in case it started after I started using the Cloudflare...

Does anyone know why this happens? Is there a better way to catch the IP user’s?

3 answers

7


Does anyone know why this happens?

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

Basically:

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

Assuming this is Ips, you will get 5.6.7.8 by the common method.


There is a better way to get the user’s IP?

Precisely because it serves as a proxy it includes the header X-Forwarded-For, but that’s not so sure, but it also includes the CF-Connecting-IP.

The header X-Forwarded-For:

Provided there is no such header defined, Cloudflare will create:

X-Forwarded-For: 1.2.3.4

Hence, 1.2.3.4 is the client’s IP.

Provided there is already one defined, 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’s IP, because the X-Forwarded-For is defined by proxy, but in practice the situation is different, because the user can fake the X-Forwarded-For, this way the last would be the true.

The header CF-Connecting-IP:

Cloudflare creates this header, it will offer the last IP, this is usually the same value as the X-Forwarded-For, in a "normal situation".


Quick fix

PHP:

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

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

However, this creates a problem, because if traffic does not pass through Cloudflare the user can fake 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’re using Apache (or any other) look for equivalent functions.

This will ensure that you can only access the website if you use Cloudflare, which will contain HTTP_CF_CONNECTING_IP, additionally NGINX will save all logs using client IP and no more proxy. ;)

3

Try it this way:

$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 it helps you!

  • 1

    I think there’s a problem with that solution.

0

I was able to get the real ip through cloudflare with the following change of the colleague’s code @Localhost:

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

/* VERIFICO SE O IP partiu do cloudflare*/
if(!empty($http_cf_connecting_ip)){
    $ip = $http_cf_connecting_ip;
/* VERIFICO SE O IP REALMENTE EXISTE NA INTERNET */
}elseif(!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;
}

Browser other questions tagged

You are not signed in. Login or sign up in order to post.