7

I do get track the "real" IP of an user, if he has an proxy wich sends the header of the real IP... does any of have a better solution, or even more headers?

Since this function is used very often in the script, it has to be very fast, and it does not seem in that constellation :/

A few suggestions I came up with, but could not realise:

  • put the headers in the order, what is used the most "in the wild", sothat the functions finishes fast
  • making the pre_match-detecting for IP faster

===

function get_real_ip()
{
  $proxy_headers = array(
                          'CLIENT_IP', 
                          'FORWARDED', 
                          'FORWARDED_FOR', 
                          'FORWARDED_FOR_IP', 
                          'HTTP_CLIENT_IP', 
                          'HTTP_FORWARDED', 
                          'HTTP_FORWARDED_FOR', 
                          'HTTP_FORWARDED_FOR_IP', 
                          'HTTP_PC_REMOTE_ADDR', 
                          'HTTP_PROXY_CONNECTION',
                          'HTTP_VIA', 
                          'HTTP_X_FORWARDED', 
                          'HTTP_X_FORWARDED_FOR', 
                          'HTTP_X_FORWARDED_FOR_IP', 
                          'HTTP_X_IMFORWARDS', 
                          'HTTP_XROXY_CONNECTION', 
                          'VIA', 
                          'X_FORWARDED', 
                          'X_FORWARDED_FOR'
                         );

  foreach($proxy_headers as $proxy_header)
  {
    if(isset($_SERVER[$proxy_header]) && preg_match("/^([1-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3}$/", $_SERVER[$proxy_header])) /* HEADER ist gesetzt und dies ist eine gültige IP */
    {
        return $_SERVER[$proxy_header];
    }
    else if(stristr(',', $_SERVER[$proxy_header]) !== FALSE) /* Behandle mehrere IPs in einer Anfrage(z.B.: X-Forwarded-For: client1, proxy1, proxy2) */
    {
      $proxy_header_temp = trim(array_shift(explode(',', $_SERVER[$proxy_header]))); /* Teile in einzelne IPs, gib die letzte zurück und entferne Leerzeichen */

      if(($pos_temp = stripos($proxy_header_temp, ':')) !== FALSE) $proxy_header_temp = substr($proxy_header_temp, 0, $pos_temp); /* Entferne den Port */

      if(preg_match("/^([1-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3}$/", $proxy_header_temp) return $proxy_header_temp;
    }
  }

  return $_SERVER['REMOTE_ADDR'];
}
Robert Harvey
  • 178,213
  • 47
  • 333
  • 501
codex
  • 99
  • 1
  • 1
  • 3
  • 3
    `ereg` is deprecated. Besides that.. you always need to store both the real ip and the "proxy" ip as users can simply add those headers on their own. – ThiefMaster Mar 24 '11 at 15:15
  • Thanks, replaced the ereg with the (possibly faster) preg_match and added a part to detect, if the field has more IPs in it like: http://en.wikipedia.org/wiki/X-Forwarded-For – codex Mar 24 '11 at 15:42
  • 1
    My real IP is `192.168.0.81`. I am curious is there any use of it? – Your Common Sense Mar 24 '11 at 15:54
  • what does it mean "used very often in the script"? is there any reason to run it more than once during script runtime? – Your Common Sense Mar 24 '11 at 15:57
  • what "IP" you gonna choose if the field has more IPs in it? Why? – Your Common Sense Mar 24 '11 at 16:12
  • it is in use via ajax, but there is now reason, why it does not have to be fast and optimized, even it is only used once ;) @192.168.0.81: does the script above on a webserver really tell you this? somethin I can not believe :P – codex Mar 24 '11 at 16:13
  • as said in http://en.wikipedia.org/wiki/X-Forwarded-For -> there is the possibility, that there are serveral IPs in the header-field, when it is passed through multiple proxies/client, which is now also handled – codex Mar 24 '11 at 16:16
  • what's wrong with 192.168.0.81 address? Why do you think its impossible to see it in the script output? Any reason you have? – Your Common Sense Mar 24 '11 at 16:30
  • In fact, only SLOW running code have to be optimized. Fast running code is fast already. Why did you mention ajax call? what's the difference between regular HTTP request and AJAX one from the server point of view? – Your Common Sense Mar 24 '11 at 16:32
  • Do you understand that all these fancy **HTTP headers** has nothing to do with TCP/IP protocol? If so, why do you call it "IP address", not "HTTP Header"? – Your Common Sense Mar 24 '11 at 16:32
  • okay, there is a possibility to have several IPs in forwarded for header. **which one** you gonna choose and why? – Your Common Sense Mar 24 '11 at 16:34
  • "okay, there is a possibility to have several IPs in forwarded for header. which one you gonna choose and why?" - i chose the first, since the definition of wikipedia... – codex Mar 24 '11 at 16:39
  • "what's wrong with 192.168.0.81 address? Why do you think its impossible to see it in the script output? Any reason you have? " - as I said >WEB – codex Mar 24 '11 at 16:42
  • "In fact, only SLOW running code have to be optimized. Fast running code is fast already. Why did you mention ajax call? what's the difference between regular HTTP request and AJAX one from the server point of view? " - since this part is called every second(on user side via AJAX), it would cost the server much RAM/CPU with lot of users....!?! – codex Mar 24 '11 at 16:44
  • your wikipedia is wrong. no order is determined anywhere, nor the form of the address or whatever format of this optional header. – Your Common Sense Mar 25 '11 at 09:56
  • so what? what if my proxy resides in the same private subnet? – Your Common Sense Mar 25 '11 at 09:57
  • do you experience any performance problem **at the moment**? is determining users IP address the only thing your script does? if not - may be you have to optimize the meaningful part of the code, not negligible one. why do you think that this code will cost the server much RAM/CPU? what about database lookups with lot of users? wouldnt it halt your server much faster than these few optional string operations? – Your Common Sense Mar 25 '11 at 10:03

2 Answers2

2

If the proxy sends a header then you can fetch the original IP of the client. If the proxy doesn't, then you can't. Unfortunately (Or maybe fortunately depending on your perspective) it's as simple as that.

What I did at our intranet, is redirect "intranet.mydomain.com" to "intranet" on the webserver, the latter doesn't use the proxy due to out internal network/DNS configuration ... Don't know what you want to do, but this may be useful.

You can also set an exclude list in the browser...

Martin Tournoij
  • 26,737
  • 24
  • 105
  • 146
1

the regex validation would fail for ipv6 addresses; so I would rather remove that (or try to find a better RegEX).

also stripos($proxy_header_temp, ':') would lead to a not expected behaivour for example for "::1" (localhost, ipv6).

my suggestion with mentioned modifications:

function getIp()
{
    $proxy_headers = array(
        'CLIENT_IP',
        'FORWARDED',
        'FORWARDED_FOR',
        'FORWARDED_FOR_IP',
        'HTTP_CLIENT_IP',
        'HTTP_FORWARDED',
        'HTTP_FORWARDED_FOR',
        'HTTP_FORWARDED_FOR_IP',
        'HTTP_PC_REMOTE_ADDR',
        'HTTP_PROXY_CONNECTION',
        'HTTP_VIA',
        'HTTP_X_FORWARDED',
        'HTTP_X_FORWARDED_FOR',
        'HTTP_X_FORWARDED_FOR_IP',
        'HTTP_X_IMFORWARDS',
        'HTTP_XROXY_CONNECTION',
        'VIA',
        'X_FORWARDED',
        'X_FORWARDED_FOR'
    );
    $regEx = "/^([1-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3}$/";
    foreach ($proxy_headers as $proxy_header) {
        if (isset($_SERVER[$proxy_header])) {
            /* HEADER ist gesetzt und dies ist eine gültige IP */
            return $_SERVER[$proxy_header];
        } else if (stristr(',', $_SERVER[$proxy_header]) !== false) {
            // Behandle mehrere IPs in einer Anfrage
            //(z.B.: X-Forwarded-For: client1, proxy1, proxy2)
            $proxy_header_temp = trim(
                array_shift(explode(',', $_SERVER[$proxy_header]))
            ); /* Teile in einzelne IPs, gib die letzte zurück und entferne Leerzeichen */

            // if IPv4 address remove port if exists
            if (preg_match($regEx, $proxy_header_temp)
                && ($pos_temp = stripos($proxy_header_temp, ':')) !== false
            ) {
                $proxy_header_temp = substr($proxy_header_temp, 0, $pos_temp);
            }
            return $proxy_header_temp;
        }
    }

    return $_SERVER['REMOTE_ADDR'];
}
tufan
  • 11
  • 2