HOWTO: Block Spam Referrers in PHP

...and general pests, by IP address block

Introduction

Being fundamentally fed up with spam Referrers, I wanted a quick and easy way to ban entire blocks of IP addresses inside PhpWiki. My requirements were simple:

  • No database tables (I'm not going to waste time doing a query for these clowns)
  • Instantly editable with vim over SSH
  • Short and efficient, so that it can be included from all my PHP code
  • Support for IP network masks or ranges
  • Support for custom messages indicating the reason of the ban

I soon gave up trying to match substrings inside URLs and trying to prevent access before the fact - it would be extremely slow and require far too much effort.

The Code

Without further ado, here it is:

// Check if a given IP address is inside a given subnet
function in_subnet($network, $mask, $ip) {
    $ip_long=ip2long($ip);
    $network_long=ip2long($network);
    $mask_long=ip2long($mask);
    if( ( $ip_long & $mask_long) == ($network_long & $mask_long)) {
        return true;
    } else {
        return false;
    }
}

// The bozos (in vim, just "/bozo" to get here)
$aBanned = array(
    array( 'ip' => "69.57.152.89",
            'mask' => "255.255.255.255",
            'reason' => "Referral spamming with URLs to spam services and porn sites" ),
    array( 'ip' => "193.55.220.0",
            'mask' => "255.255.255.0",
            'reason' => "Referral spamming with drug advertising" )
);

// inline code (this gets executed by whoever includes this file)
foreach( $aBanned as $aCheck ) {
    if( in_subnet( $aCheck['ip'], $aCheck['mask'], $_SERVER['REMOTE_ADDR'] ) ) {
        header( "HTTP/1.1 403 Access Denied" );
        echo( "<H1>You Are Banned From Accessing This Site</H1>");
        echo( "<p><b>Reason:</b> " . $aCheck['reason'] . "</p>" );
        echo( "<p>I am in the process of reporting this abuse to your ISP or netblock owner" );
        echo(" - on the Internet, nobody is really anonymous.</p>" );
        // log this so we can grep it out of error_log
        error_log( strftime("%d/%b/%Y:%H:%M:%S") . " [BANNED] " . $aCheck['ip'] . " " . $aCheck['reason'] );
        exit;
    }
}

See Also:

PHP