views:

396

answers:

4

I would like to know whether there is a way of restricting the users of a site such that they can only access the inner pages of a site if they are within a certain range of IP addresses or a certain network?

The current PHP scripts I am getting cant differentiate the real IPs from the Proxies?

Thanks

+3  A: 

i wouldn’t restrict on ip addresses. as you said, you can’t know if it’s a proxy. furthermore, ip addresses can be easily spoofed.

knittl
Thanks for your answer, would you have any suggestions on how to restrict entry to a site based on location?
Stanley Ngumo
no, i’m afraid not. you’ll have to use ip addresses, but there’s no way of “blocking proxies” that i know of
knittl
It should be sufficient to use verify the IP address in addition to using .htaccess files with passwords. http://www.htaccesstools.com/htpasswd-generator/
jimiyash
but then you could use passwords only …
knittl
+3  A: 

Have you considered using apache .htaccess files for that? http://stackoverflow.com/questions/593922/ip-restriction-with-htaccess

Miha Hribar
A: 

Proxy servers should set the X-Forwarded-For HTTP header, which you could look up with $_SERVER['HTTP_X_FORWARDED_FOR']. Otherwise $_SERVER['REMOTE_ADDR'] can be used to get the IP address. As others have noted, both of these can be easily spoofed, and there is no requirement for proxies to set the X-Forwarded-For request header.

There is a ip2long() function in PHP which give you an integer to use for range checking.

To get the location of an IP address you need a lookup table which maps IP address ranges to approximate geographical locations (such lookup tables are typically not free). There are many services which offer IP address geolocation, some of which are mentioned here and here.

Øystein Riiser Gundersen
+2  A: 

You can try out a script I created that allows very advanced IP rules. I coded it years ago so I apologize in advance for the current shape of it.

Edit: If you're looking for an "&" operator in the syntax don't bother. I forgot to add it when I coded this and looking back at this script now makes me cringe at the thought of touching it again.

<?php
##############################################################
# IP Expression Class                                        #
# Easy IP-based Access Restrictions                          #
# Change Log:                                                #
# - Added Range and limited IPv6 support                     #
# - Changed name from IPAR to IPEX                           #
#                                #
##############################################################
# Example Rules:                                             #
# 69.[10-20].[^50].*                                         #
# 69.*.[1-5 | 10-20 |^30].*                                  #
# 60.12.2.*                                                  #
# 127.*                                                      #
# 69.1.1.1-70.1.1.1   <-- This is a range                    #
#                                                            #
# Usage:                                                     #
# Ipex::IsMatch($rule, $ip);                                 #
#                                                            #
# [range] - Defines a range for a section of the IP          #
# |   - OR token. IP can match this range/number         #
# ^       - NOT token. IP can not match this range/number    #
# x-y     - Defines a range from x to y                      #
# x       - Exactly match x (x = a hex or dec number)        #
# *       - Match any number                                 #
#                                #
#----------===============================-------------------#
#           [ Written by Chris Tarquini ]                    #
#----------===============================-------------------#
##############################################################

define('IPR_DENY', false);
define('IPR_ALLOW', true);
define('IPR_ERR_MISMATCH',-1);
define('IPR_ERR_RANGE_MISMATCH',-2);
define('IPR_ERR_RANGE_INVALID',-3);
define('IPR_ERR_INVALID_RULE',-4);

class IPEX
{

    const TOKEN_RANGE_BEGIN = '[';
    const TOKEN_RANGE_END = ']';
    const TOKEN_WILDCARD = '*';
    const TOKEN_RANGE_SPLIT = '-';
    const TOKEN_OR = '|';
    const TOKEN_NOT = '^';
    const DEBUG_MODE = TRUE;

    private static function trace($err){if(self::DEBUG_MODE) echo "$err\r\n";}
    private static function FixRule($rule,$count = 4, $split='.')
    {
        $rule = explode($split,$rule);
        $filler = 0;
        $size = sizeof($rule);
        for($i = 0; $i < $count; $i++)
        {
            if($i > $size) { $rule[] = $filler; $size++;}
            else if(empty($rule[$i])) { $filler = self::TOKEN_WILDCARD; $rule[$i] = $filler;}
        }
        return $rule;   
    }

    private static function FixIP($rule,$count = 4, $split='.')
    {
        $rule = explode($split,$rule);
        $size = sizeof($rule);
        for($i = 0;  $i < $count; $i++)
        {
            if($i > $size) { $rule[] = 0; $size++;}
            else if(empty($rule[$i])) { $rule[$i] = 0;}
        }
        return $rule;   
    }
    private static function GetIpType(&$ip)
    {
        $mode = IPID::Identify($ip,$newip);
        if($mode == IPID_IPv4_Embed) { $ip = $newip; return IPID_IPv4;}
        return $mode;
    }
    private static function FixIPRange(&$start, &$stop)
    {
        $count = 4; $split = '.';
        if(self::GetIpType($start) == IPID_IPv6) {$count = 8; $split = ':';}

        $q = 0;
        while($q < 2)
        {
            $filler = ($q == 0) ? 0 : 255;
            $arr = explode($split,($q == 0) ? $start : $stop);
            $size = sizeof($arr);
            for($i = 0; $i < $count; $i++)
            {
                if($i > $size){ $arr[] = $filler; $size++;}
                else if(empty($arr[$i])){ $arr[$i] = $filler; }
            }
            if($q == 0) $start = implode($split, $arr);
            else $stop = implode($split,$arr);
            $q++;
        }

    }
    public static function IsInRange($start, $stop, $ip)
    {
        //Sorry guys we only support IPv4 for this ;(
        self::FixIPRange($start,$stop);
        self::trace("fixed: start = $start, stop = $stop");
        $start = ip2long($start); $stop = ip2long($stop);
        $ip = ip2long($ip);
        self::trace("start = $start, stop = $stop, ip = $ip");
        return ($ip >= $start && $ip <= $stop);
    }
    public static function IsAllowed($rule, $ip){return self::IsMatch($rule,$ip);}
    public static function IsMatch($rule,$ip)
    {
        $mode = self::GetIpType($ip);
        self::trace("ip type: $mode");
        if(strpos($rule, self::TOKEN_RANGE_SPLIT) !== false && strpos($rule,self::TOKEN_RANGE_BEGIN) === false)
        {
            self::trace("ip range mode");
            $test = explode(self::TOKEN_RANGE_SPLIT, $rule);
            self::trace("range size: ".sizeof($test));
            print_r($test);
            if(sizeof($test) != 2) return IPR_ERR_RANGE_INVALID;
            $start = $test[0]; $end = $test[1];
            if(empty($start) || empty($end)) return IPR_ERR_RANGE_INVALID;
            self::trace("range start: $start, range stop: $end");
            $rm1 = (self::IsHex($start)) ? $mode : self::GetIpType($start);
            $rm2 = (self::IsHex($end)) ? $mode : self::GetIpType($end);
            self::trace("range types: $rm1, $rm2\r\nip type: $mode");
            if($rm1 != $rm2  || $rm1 != $mode) return IPR_ERR_RANGE_MISMATCH;
            if($mode == IPID_IPv6) { return IPR_ERR_IPv6_NOTSUPPORTED;}     
            return self::IsInRange($start,$end,$ip);
        }

        if(self::GetIpType($rule) != $mode) return IPR_ERR_MISMATCH;


        //all is good so far
        $count = 4;
        $split = '.'; if($mode==IPID_IPv6){$count = 8; $split=':';}
        $rule = self::FixRule($rule, $count,$split);

        $ip = self::FixIp($ip,$count,$split);
        self::trace("ip: ".implode($split,$ip));
        self::trace('rule: '.implode($split,$rule));
        for($i = 0; $i < $count; $i++)
        {
            $r = str_replace(' ', '', $rule[$i]);
            $ri = false;
if($r == self::TOKEN_WILDCARD) continue;
            if($mode == IPPID_IPv6 && self::IsHex($r)) { $ri = hexdec($r);}else if(is_numeric($r)) $ri = $r;


            $x = $ip[$i];
            if($mode == IPPID_IPv6) $x = hexdec($x);
            //* Exact Match *//
            self::trace("rule[$i]: $ri");
            self::trace("ip[$i]: $x");
            if($ri !== false && $ri != $x) return IPR_DENY;
            $len = strlen($r);
            for($y = 0; $y < $len; $y++)
            {
                self::trace("y = $y");
                if(substr($r, $y,1) == self::TOKEN_RANGE_BEGIN)
                {
                    ++$y;
                    self::trace("found range, y = $y");
                    $negflag = false;
                    $start = false;
                    $stop = false;
                    $allows = 0;
                    $denys = 0;
                    $q = 0;
                    $c = substr($r,$y,1);
                    while($c !== false)
                    {
                        self::trace("in range, char: $c");
                        //* Flags *//
                        $break = false;
                        $exec = false;
                        $toggle = false;
                        $reset = false;

                        if($c === self::TOKEN_RANGE_END) {$skiphex = true;$break = true; $exec = true; self::trace("found end of range");}
                        if($c === self::TOKEN_NOT) {if($q > 0){ $toggle = true; $exec = true;} else $negflag = !$negflag; $skiphex =false; self::trace("found TOKEN_NOT");}
                        if($c === self::TOKEN_OR) { $exec = true; $reset = true;$skiphex=true;self::trace("found TOKEN_OR");} 
                        if($c === self::TOKEN_RANGE_SPLIT){ $skiphex = false;++$q; self::trace("found range split");}

                        //* Read Hex Tokens *//
                        if(!$skiphex && self::IsHexChar($c))
                        {
                            $n = self::ReadNextHexToken($r,$y);
                            if($mode == IPID_IPv6) $n = hexdec($n);
                            if($q == 0) $start = $n;
                            else if($q == 1) $stop = $n;
                            --$y; //fixes error
                            self::trace("parsed number: $n, y = $y");
                        }   
                        if($reset) {$negflag = false; $start = false; $stop = false;  $q = 0;}
                        if($exec)
                        {
                            self::trace("executing: start = $start, stop = $stop, x = $x");
                            self::trace("negflag = $negflag");
                            if($stop !== false && $x >= $start && $x <= $stop)
                            {
                                if($negflag) { ++$denys; $allows = 0; break;}
                                else ++$allows;
                            }
                            else if($stop === false && $start == $x)
                            {
                                if($negflag) { ++$denys; $allows = 0; break;}
                                else ++$allows;
                            }
                            self::trace("exec complete: allows = $allows, denys = $denys"); 
                            $q = 0;
                        }
                        if($toggle) $negflag = !$negflag;
                        if($break) break;
                        ++$y;
                        $c = substr($r,$y,1);
                    } 
                    if(!$allows) return IPR_DENY;
                }
            }


        }
                return IPR_ALLOW;   

    }
    private static function ReadNextHexToken($buff, &$offset, $max = -1)
    {
        $str = '';
        if($max == -1) { $max = strlen($buff);}
        for(; $offset < $max; $offset++)
        {
            $c = substr($buff,$offset, 1);
            if(self::IsHexChar($c))
                $str .= $c;
            else
                return $str;
        }
        return $str;
    }
    private static function IsHex($x){ $len = strlen($x); for($i = 0; $i < $len; $i++) if(!self::IsHexChar(substr($x,$i,1))) return false; return true;}
    private static function IsHexChar($x){self::trace("isHex($x);"); return (in_array(strtoupper($x),array('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F')));
}
}



######################
# IP Identify Class  #
#####################
define('IPID_INVALID',false);
define('IPID_IPv4',2);
define('IPID_IPv6',3);
define('IPID_IPv4_Embed',6);

class IPID
{

        public static function Identify($ip,&$ipconvert = false)
        {
                $ip = strtoupper($ip);
                $ipconvert = $ip;
                // Check if we are IPv4
                if(strpos($ip,':') === false && strpos($ip,'.') !== false)
                        return IPID_IPv4;
                //Is it one of those fucked up hybrid mother fuckers?
                else if(strpos($ip,':FFFF') !== false && strpos($ip,'.') !== false)
                {
                        $ipconvert = substr($ip,strpos($ip,':FFFF:')+6);
                        return IPID_IPv4_Embed;
                }
                // Is it IPv6?
                else if(strpos($ip,':') !== false)  return IPID_IPv6;
                // What the fuck?
                return IPID_INVALID;
        }


}
?>

You can use it as long as you don't try and resell it and you keep the header as is.

Chris T