Hi,

I found this code on the web and don't fully understand it.

What are all these pregmatches doing, what are they good for and why are they useful?

What does that reversing refer to and do?

And finally: if I think of including a check for a change in the IP as an (optional) security feature to an authentication class....is it wise to go through a process as below to get the IP to check against, is it overkill, or is it simply the wrong way?

thx in advance

Bjom

<?php //found here: http://www.dreamincode.net/code/snippet1745.htm
	  // indenting/linewrapping changed by me (Bjom)
	function getIP()
	{
		// Find the user's IP address. (but don't let it give you 'unknown'!)
		if (!empty($_SERVER['HTTP_X_FORWARDED_FOR']) && !empty($_SERVER['HTTP_CLIENT_IP']) 
			&& (preg_match('~^((0|10|172\.16|192\.168|255|127\.0)\.|unknown)~', $_SERVER['HTTP_CLIENT_IP']) == 0 
				|| preg_match('~^((0|10|172\.16|192\.168|255|127\.0)\.|unknown)~', $_SERVER['REMOTE_ADDR']) != 0)) {
			// We have both forwarded for AND client IP... check the first forwarded for as the block - only switch if it's better that way.
			if (strtok($_SERVER['HTTP_X_FORWARDED_FOR'], '.') != strtok($_SERVER['HTTP_CLIENT_IP'], '.') 
				&& '.' . strtok($_SERVER['HTTP_X_FORWARDED_FOR'], '.') == strrchr($_SERVER['HTTP_CLIENT_IP'], '.') 
				&& (preg_match('~^((0|10|172\.16|192\.168|255|127\.0)\.|unknown)~', $_SERVER['HTTP_X_FORWARDED_FOR']) == 0 
					|| preg_match('~^((0|10|172\.16|192\.168|255|127\.0)\.|unknown)~', $_SERVER['REMOTE_ADDR']) != 0)) {
				$_SERVER['REMOTE_ADDR'] = implode('.', array_reverse(explode('.', $_SERVER['HTTP_CLIENT_IP'])));
			} else {
				$_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_CLIENT_IP'];
			}
		}
		if (!empty($_SERVER['HTTP_CLIENT_IP']) 
			&& (preg_match('~^((0|10|172\.16|192\.168|255|127\.0)\.|unknown)~', $_SERVER['HTTP_CLIENT_IP']) == 0 
				|| preg_match('~^((0|10|172\.16|192\.168|255|127\.0)\.|unknown)~', $_SERVER['REMOTE_ADDR']) != 0)) {
			// Since they are in different blocks, it's probably reversed.
			if (strtok($_SERVER['REMOTE_ADDR'], '.') != strtok($_SERVER['HTTP_CLIENT_IP'], '.')) {
				$_SERVER['REMOTE_ADDR'] = implode('.', array_reverse(explode('.', $_SERVER['HTTP_CLIENT_IP'])));
			} else {
				$_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_CLIENT_IP'];
			}
		} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
			// If there are commas, get the last one.. probably.
			if (strpos($_SERVER['HTTP_X_FORWARDED_FOR'], ',') !== false) {
				$ips = array_reverse(explode(', ', $_SERVER['HTTP_X_FORWARDED_FOR']));

			// Go through each IP...
			foreach ($ips as $i => $ip)	{
				// Make sure it's in a valid range...
				if (preg_match('~^((0|10|172\.16|192\.168|255|127\.0)\.|unknown)~', $ip) != 0 && preg_match('~^((0|10|172\.16|192\.168|255|127\.0)\.|unknown)~', $_SERVER['REMOTE_ADDR']) == 0)
					continue;

				// Otherwise, we've got an IP!
				$_SERVER['REMOTE_ADDR'] = trim($ip);
				break;
			}
		// Otherwise just use the only one.
		} elseif (preg_match('~^((0|10|172\.16|192\.168|255|127\.0)\.|unknown)~', $_SERVER['HTTP_X_FORWARDED_FOR']) == 0 
				  || preg_match('~^((0|10|172\.16|192\.168|255|127\.0)\.|unknown)~', $_SERVER['REMOTE_ADDR']) != 0)
			$_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_X_FORWARDED_FOR'];
	} elseif (!isset($_SERVER['REMOTE_ADDR'])) {
		$_SERVER['REMOTE_ADDR'] = '';
}
?>

    The regexps check for invalid IP addresses. E.g. 192.168 and 10. are reserved for intranets and will never be used on the internet. Still, they may show up in data sent by headers, or for intranet requests.

    I actually have no idea about the HTTP_CLIENT_IP array key or what it represent. Another "possibly added through headers" field? Perhaps someone would care to enlighten me here.

    But disregarding what I don't know... The only thing you can be certain of is REMOTE_ADDR, since this value is always present and always represents whatever computer sent the request. This, however, might just as well be a proxy as the actual user.

    HTTP_X_FORWARDED_FOR may be present, but there is no guarantee. (some) proxies add to this value, which is contained in the header unless I'm mistaken, and either way this information can intentionally be changed.
    What a nice proxy does, is prepend the IP address which sent the request to the proxy, before passing the request on. As such, at the first proxy, this is the user's IP, at the second proxy this contains "proxy1 IP, client IP", then "proxy2 IP, proxy1 IP, client IP". Hence the array_reverse in this case.

    As for the other array_reverse calls that are found after the strok() != strok() comparison, it's to deal with IP addresses being reversed when they are not in the same block. Or some such... Once again, my networking knowledge isn't good enough. I don't know why or when the IP is reversed, just that it may be reversed.

    So, is it worth doing all these things? Possibly. It all depends on your needs. One thing that might be nice is to log both (last) proxy and user ip. That way, you can decide to block a certain proxy if you get a lot of crap requests coming through it. That is, if HTTP_X_FORWARDED_FOR is set, log the last address from it as user ip, but also log REMOTE_ADDR as proxy ip. Else, just log REMOTE_ADDR as user (even though it might be a proxy).

      Thanks for that info, so far.

      There are still three points that are open/need confirmation:

      why would the ip get reversed?
      what does the HTTP_CLIENT_IP stand for and when is it send?
      will the client be the first or the last ip in the HTTP_X_FORWARDED_FOR list?

      Bjom

        yeah yeah, that I should have dug up myself:o

        Thanks for the linky. I had gotten some contradictory/confusing info on what gets appended, prepended etc...

        I'll come back and post my findings once I know what would cause reversal and what that HTTP_CLIENT_IP is good for. perhaps tomorrow...

          Write a Reply...