2

Currently I get usr's IP like this:

if ( isset($_SERVER['HTTP_X_FORWARDED_FOR']) ){
    $ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
} elseif ( isset($_SERVER['REMOTE_ADDR']) ) {
    $ip = $_SERVER['REMOTE_ADDR'];
}

// IPs
+----+----------------+-------------+
| id |    user_ip     |  date_time  |
+----+----------------+-------------+
| 1  | 43.12.9.9      | 1468070172  |
| 2  | 173.3.0.1      | 1468070667  |
+----+----------------+-------------+

But now, I read this in here:

if you are going to save the $_SERVER['HTTP_X_FORWARDED_FOR'], make sure you also save the $_SERVER['REMOTE_ADDR'] value. E.g. by saving both values in different fields in your database.

So I'm changing my code to this:

$remote_add = $_SERVER['REMOTE_ADDR']; // I don't use isset() for this becase it is always set
$http_x_forwarded_for = isset( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ? $_SERVER['HTTP_X_FORWARDED_FOR'] : null;

// IPs
+----+----------------+----------------+-------------+
| id |    remote      |   forwarded    |  date_time  |
+----+----------------+----------------+-------------+
| 1  | 43.12.9.9      | NULL           | 1468070172  |
| 2  | 93.35.40.1     | 173.3.0.1      | 1468070667  |
+----+----------------+----------------+-------------+

Ok what I'm doing is correct? I'm asking this because a few professional programmers tell me what you doing is useless and you need just on column as user's IP. Well may please someone clear me up? How can I get user's IP correctly?


My final code:

foreach (array('HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED') as $key){
  if (array_key_exists($key, $_SERVER) === true){
    foreach (explode(',', $_SERVER[$key]) as $header){
      $header = trim($header); // just to be safe
      if (filter_var($header, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false){
        $header = $header;
      }
    }
  }
}

$user_ip = $_SERVER['REMOTE_ADDR'];

// IPs
+----+----------------+----------------+-------------+
| id |    user_ip     |     header     |  date_time  |
+----+----------------+----------------+-------------+
| 1  | 43.12.9.9      | NULL           | 1468070172  |
| 2  | 93.35.40.1     | 173.3.0.1      | 1468070667  |
+----+----------------+----------------+-------------+
Community
  • 1
  • 1
Martin AJ
  • 6,261
  • 8
  • 53
  • 111

1 Answers1

4

In a http-request, someone can spoof ANY information, except for the remote address. You cannot complete the tcp handshake without actually having access to the modem on that ip-address, or a computer behind that modem/router. The REMOTE_ADDR key is always set to the device that made the connection to your server. In some cases that device is the proxy that was used by the user.

The HTTP_X_FORWARDED_FOR header is a header that nice proxies use to signify what the originating ip-address was that ordered the proxy to load a page on their behalf. This header can however be spoofed. Someone can send any information in this header, including fun things such as 127.0.0.1 (localhost), or even html or scripts for servers that just assume that field can only contain numbers and dots, or even raw sql statements for servers that do not use prepared queries to insert this stuff.


The only information that is guaranteed correct is the remote ip, but it is not necessarily the user's ip. If the remote ip is a proxy, logging the remote ip might allow you to contact the owner of the remote proxy of fraudulent behaviour, to let them search their logs which ip actually made that request. The forwarded ip can in some cases safe you time in cases where the proxy was not intended to obscure the identity of the person that used the proxy, which would allow you to take some measures against that ip, instead of the proxy itself.

Making an educated guess, by sometimes using the HTTP_X_FORWARDED_FOR header and sometimes using the REMOTE_ADDR, will result in unusable data. You can not know which data you can trust, and which data you can not trust.

I'm asking this because a few professional programmers tell me what you doing is useless and you need just on column as user's IP.

If what you are doing is useless depends completely on what you are using this for. If you are displaying the forwarded field, remember to properly escape all special characters for the context in which you display it. If you would only store one ip, it would be the remote ip, ignoring the HTTP_X_FORWARDED_FOR header completely. It is the only information you can completely trust.

Sumurai8
  • 20,333
  • 11
  • 66
  • 100
  • 1
    Great explanation .. +1 .. Just still I don't know how should I write the code exactly, I mean the order of those conditions: `if ( isset($_SERVER['HTTP_X_FORWARDED_FOR']) ){` should be the first of or `if ( isset($_SERVER['REMOTE_ADDR']) ) {` ? – Martin AJ Jul 09 '16 at 16:41
  • In situations where logging as much information as possible is important, go for your second approach. (e.g. the CheckUser plugin for MediaWiki projects logs the xff headers to be used to give better information on vandals that need to be banned.) In situations where you want to have guaranteed correct data, use only the remote_addr `$ip = $_SERVER['REMOTE_ADDR']`, as it is always there, and always contains the ip of the host that contacted your server. It is the ip that shows up in the edit history of Wikipedia for example, regardless if that ip is a proxy. – Sumurai8 Jul 09 '16 at 16:51
  • Don't use the first block of code, unless you want to log some javascript code when I visit your site ;-) ... Or 127.0.0.1 if you add additional checks if the header actually contains some kind of ip, or list of ip's. – Sumurai8 Jul 09 '16 at 16:55
  • Well [here](https://gist.github.com/anonymous/f449ce7b242d599339c0514cab37c240) is my final code. I store both `REMOTE_ADDR` and one of http headers in two different columns. Is that fine? – Martin AJ Jul 09 '16 at 17:20
  • I'll try to remind myself to look at it when I am back home. Can't open github here. – Sumurai8 Jul 09 '16 at 17:23
  • Ah ... I've updated my question and added the final code. – Martin AJ Jul 09 '16 at 17:25
  • `explode(",", ...)` is not a valid way to parse most of these headers. You are using `$header` in the foreach loop, so overwriting it does not make much sense. I don't understand why you don't take the first (probably best) match, and instead loop through every possible header, where for example `HTTP_FORWARDED_FOR` should never exist. If you want more help with this, I recommend asking a new question. – Sumurai8 Jul 10 '16 at 11:58