7

I'm trying to put a couple of lines into a page of mine to redirect users not matching a certain set of IP addresses.

Here it is:

$whitelist = array('111.111.111.111', '112.112.112.112');
if (!(in_array($_SERVER['REMOTE_ADDR'], $whitelist))) {
  header('Location: http://asdf.com');
}

It works fine when the full address is known, but how can I make this utilize wildcards and work on IP ranges?

Phiter
  • 14,570
  • 14
  • 50
  • 84
AKor
  • 8,550
  • 27
  • 82
  • 136
  • Best way would be to populate the array from a database list with all the allowed IPs. – Phiter Feb 22 '16 at 16:47
  • 1
    writing more code would be a good start. – Marc B Feb 22 '16 at 16:48
  • 2
    Possible duplicate of [How to check an IP address is within a range of two IPs in PHP?](http://stackoverflow.com/questions/11121817/how-to-check-an-ip-address-is-within-a-range-of-two-ips-in-php) – Phiter Feb 22 '16 at 16:48
  • For checking ranges i'd suggest to look at **ip2long()** – hherger Feb 22 '16 at 16:53

3 Answers3

10

You could make a function to check the user's ip is allowed.

function isAllowed($ip){
    $whitelist = array('111.111.111.111', '112.112.112.112', '68.71.44.*');

    // If the ip is matched, return true
    if(in_array($ip, $whitelist)) {
        return true;
    }

    foreach($whitelist as $i){
        $wildcardPos = strpos($i, "*");

        // Check if the ip has a wildcard
        if($wildcardPos !== false && substr($ip, 0, $wildcardPos) . "*" == $i) {
            return true;
        }
    }

    return false;
}

And then use the function

if (! isAllowed($_SERVER['REMOTE_ADDR'])) {
    header('Location: http://asdf.com');
}
Chin Leung
  • 14,621
  • 3
  • 34
  • 58
3

The best way is use CIDR:

<?php
  function ipCIDRCheck ($IP, $CIDR) {
    list ($net, $mask) = split ("/", $CIDR);

    $ip_net = ip2long ($net);
    $ip_mask = ~((1 << (32 - $mask)) - 1);

    $ip_ip = ip2long ($IP);

    $ip_ip_net = $ip_ip & $ip_mask;

    return ($ip_ip_net == $ip_net);
  }
  echo ipCheck ("192.168.1.23", "192.168.1.0/24");
?>
3

To fix and explain @Chin Leung's answer:

/**
 * Returns if the given ip is on the given whitelist.
 *
 * @param string $ip        The ip to check.
 * @param array  $whitelist The ip whitelist. An array of strings.
 *
 * @return bool
 */
function isAllowedIp($ip, array $whitelist)
{
    $ip = (string)$ip;
    if (in_array($ip, $whitelist, true)) {
        // the given ip is found directly on the whitelist --allowed
        return true;
    }
    // go through all whitelisted ips
    foreach ($whitelist as $whitelistedIp) {
        $whitelistedIp = (string)$whitelistedIp;
        // find the wild card * in whitelisted ip (f.e. find position in "127.0.*" or "127*")
        $wildcardPosition = strpos($whitelistedIp, "*");
        if ($wildcardPosition === false) {
            // no wild card in whitelisted ip --continue searching
            continue;
        }
        // cut ip at the position where we got the wild card on the whitelisted ip
        // and add the wold card to get the same pattern
        if (substr($ip, 0, $wildcardPosition) . "*" === $whitelistedIp) {
            // f.e. we got
            //  ip "127.0.0.1"
            //  whitelisted ip "127.0.*"
            // then we compared "127.0.*" with "127.0.*"
            // return success
            return true;
        }
    }
    // return false on default
    return false;
}

Tests

$whitelist = [
    '111.111.111.111',
    '112.112.112.112',
    '68.71.44.*',
    // '*' would allow any ip btw
];
var_dump(isAllowedIp('68.71.44.11', $whitelist)); // true
var_dump(isAllowedIp('68.71.44.12', $whitelist)); // true
var_dump(isAllowedIp('68.71.14.12', $whitelist)); // false
cottton
  • 1,522
  • 14
  • 29
  • can i use multiple `*` like ip address `10.10.*.*` –  Feb 25 '19 at 11:10
  • 1
    @FarhanDharsi Yes and No. `substr($ip, 0, $wildcardPosition)` detects the position and compares then both at the same length. See the comment: `(f.e. find position in "127.0.*" or "127*")`. So you can use only 1 wildcard (which is in your example working. But 127.*.10.* would require more work|load (regex or explode)) – cottton Feb 26 '19 at 02:00