14

I'd like, for example, block every IP from base 89.95 (89.95..). I don't have .htaccess files on my server, so I'll have to do it with PHP.

if ($_SERVER['REMOTE_ADDR'] == "89.95.25.37") die();

Would block specific IP. How can I block entire IP blocks?

iTayb
  • 12,373
  • 24
  • 81
  • 135

8 Answers8

19

Try strpos()

if(strpos($_SERVER['REMOTE_ADDR'], "89.95") === 0)
{
    die();
}

If you notice, the === operator makes sure that the 89.95 is at the beginning of the IP address. This means that you can specify as much of the IP address as you want, and it will block no matter what numbers come after it.

For instance, all of these will be blocked:

89.95 -> 89.95.12.34, 89.95.1234.1, 89.95.1.1
89.95.6 -> 89.95.65.34, 89.95.61.1, 89.95.6987

(some of those aren't valid IP addresses though)

HoldOffHunger
  • 18,769
  • 10
  • 104
  • 133
Tyler Carter
  • 60,743
  • 20
  • 130
  • 150
  • hehe, i'm surprised that `strpos` here works faster than `substr` – zerkms May 19 '10 at 22:42
  • @zerkms it shouldn't really be surprising - `strpos` can return immediately if the test fails, and does not have to do any string copying or allocation. Also, I just wanted to note that the behaviour changes if you switch `===` for `==`, so don't do that. Use three equal signs. – gnud May 19 '10 at 23:09
  • @gnud: for long strings strpos would be slower, just because it should iterate over the whole string to search the needle. and i know what is the difference of `===` and `==`. – zerkms May 19 '10 at 23:29
  • @zerkms Yes, the `==` vs `===` was meant as a separate note to complement the answer, not aimed at you :=) – gnud May 19 '10 at 23:35
5

Use ip2long() to convert dotted decimal to a real IP address. Then you can do ranges easily.

Just do ip2long() on the high and low range to get the value, then use those as constants in your code.

If you're familiar with subnet masking, you can do it like this:

// Deny 10.12.*.*
$network = ip2long("10.12.0.0");
$mask = ip2long("255.255.0.0");
$ip = ip2long($_SERVER['REMOTE_ADDR']);
if (($network & $mask) == ($ip & $mask)) {
  die("Unauthorized");
}

Or if you're familiar with this format 10.12.0.0/16:

// Deny 10.12.*.*
$network = ip2long("10.12.0.0");
$prefix = 16;
$ip = ip2long($_SERVER['REMOTE_ADDR']);
if ($network >> (32 - $prefix)) == ($ip >> (32 - $prefix)) {
  die("Unauthorized");
}

You can turn these into functions and have very manageable code, making it easy to add IP addresses and customize the ranges.

YakovL
  • 7,557
  • 12
  • 62
  • 102
Marcus Adams
  • 53,009
  • 9
  • 91
  • 143
  • Thanks! This is how we're doing it. – Ami May 22 '12 at 12:46
  • Investigating just `$_SERVER{'REMOTE_HOST'}` would be enough? Because maybe the user uses a proxy. So there is some other parameters that you have to check, like `REMOTE_ADDR` or [these](http://stackoverflow.com/questions/15699101/get-the-client-ip-address-using-php#15699240). Am I right? or doing what I linked isn't useful? – stack Jun 30 '16 at 23:25
  • Yes, just use REMOTE_ADDR. – Marcus Adams Jul 01 '16 at 02:17
3

Convert the dotted quad to an integer:

$ip = sprintf('%u', ip2long($_SERVER['REMOTE_ADDR']));

// only allow 10.0.0.0 – 10.255.255.255
if (!($ip >= 167772160 && $ip <=  184549375)) {
    die('Forbidden.');
}
webbiedave
  • 48,414
  • 8
  • 88
  • 101
  • what is the reason of using integers here? making code more obfuscated? – zerkms May 19 '10 at 22:32
  • Not that this diminishes the usefulness of this answer, but Authorization sounds more of a Opt-In word, where a select group of people are authorized. In this case, instead of only authorizing a few people, he unauthorizes, or forbids a few people. As such, I would think a better word would be 'Forbidden'. – Tyler Carter May 19 '10 at 22:34
  • @webbiedave: lol. again: what is the reason of using integers here? – zerkms May 19 '10 at 22:37
  • 1
    @zerkms, how else will you handle a class A or B range? You will be writing a lot of code if you keep the IP address in dotted decimal format. – Marcus Adams May 19 '10 at 22:40
  • @Marcus: huh? the question is: "how to block anyone with IP started with 89.95". Nothing about network classes. That's why I asked what is the reason of using integers **here**, in this particular case. – zerkms May 19 '10 at 22:44
  • @zerkms: OP: "I'd like, for example". Why not show him a way to do even more? – webbiedave May 19 '10 at 22:45
  • @webbiedave: still nothing about classes – zerkms May 19 '10 at 22:47
  • "Why not show him a way to do even more?": because for this specific question string functions are good enough. But after the answers like yours newbies use integer presentation of IP anywhere they can (instead of strings) and make obfuscated code. just because someone somewhere proposes them to **do in this way** without explanation of applicability of this method ;-) – zerkms May 19 '10 at 22:52
  • @zerkms: Thanks again for the insights. I stand by my answer. – webbiedave May 20 '10 at 00:30
  • 1
    @zerkms, the question reads "How can I block entire IP blocks?" – Marcus Adams May 20 '10 at 12:13
1
$user_ip = $_SERVER['REMOTE_ADDR']; // get user ip

$denyIPs = array("111.111.111", "222.222.222", "333.333.333");
if (in_array ($user_ip, $denyIPs)) {
   // blocked ip
}
else {
   // not blocked
}
Sumith Harshan
  • 6,325
  • 2
  • 36
  • 35
1

This has always worked very well for me: This checks for the proper server variables and compares it against a list of known IPs.. and yes, PHP does understand wildcards, so using * within the IP with assist in blocking ranges of IPs.

// The blacklisted ips.
$denied_ips = array(
'1.2.3.4',
'2.3.*',
);

// The function to get the visitor's IP.
function getUserIP(){
    //check ip from share internet
    if (!empty($_SERVER['HTTP_CLIENT_IP'])){
      $ip=$_SERVER['HTTP_CLIENT_IP'];
    }
    //to check ip is pass from proxy
    elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])){
      $ip=$_SERVER['HTTP_X_FORWARDED_FOR'];
    } else {
      $ip=$_SERVER['REMOTE_ADDR'];
    }
    return $ip;
}
//The user
$visitorIp = getUserIP();

// Now let's search if this IP is blackliated
$status = array_search($visitorIp, $denied_ips);

// Let's check if $status has a true OR false value.
if($status !== false){
    echo '<div class="error">Your IP has been banned! Stop spamming us!</div>';
    // header("Location: http://zombo.com");
    // exit; 
}

There's also a great article at Perishable Press: http://perishablepress.com/how-to-block-ip-addresses-with-php/

revive
  • 831
  • 2
  • 15
  • 31
  • 1
    "and yes, PHP does understand wildcards, so using * within the IP with assist in blocking ranges of IPs." are you sure about that? specific version of php maybe? – artfulhacker Apr 12 '13 at 23:02
1

Make a substring :) For example for blocking 89.95.25.* you make a substring of the IP, cutting off the last two numbers and compare it to "89.95.25."

Samuel
  • 18,286
  • 18
  • 52
  • 88
0

using revive's code, use this to get wildcard search working

// Now let's search if this IP is blackliated
$status = false;
foreach($denied_ips as $val)
{
    if (strpos($val,'*') !== false)
    {
        if(strpos($visitorIp, array_shift(explode("*", $val))) === 0)
        {
            $status = true;
            break;
        }
    }
    else
    {
        if(strcmp($visitorIp, $val) === 0)
        {
            $status = true;
            break;
        }
    }
}
artfulhacker
  • 4,823
  • 1
  • 37
  • 31
-1
$deny = array("111.111.111", "222.222.222", "333.333.333");

if (in_array($_SERVER['REMOTE_ADDR'], $deny)) {
    header("location:http://www.google.com/");
    exit();
}
smottt
  • 3,272
  • 11
  • 37
  • 44