Extremely Tiny Application Firewall Class

This is a small IP address blocking “firewall” for an old PHP based website. It’s totally basic, with no features at all. This is for those annoying situations when old software contains security holes, and you find out in the logs, when you see some queries with a long string of SQL-like code.

This is written for an old codebase with a bespoke db abstraction layer, so you will need to modify it to use PDO or a native interface, or some other data store (like memcached).

<?php // vim:ai:ts=4:sw=4:st=4:et

/**
 * Static class to add IP addresses to the firewall table,
 * and see if the IP address is in the table.
 *
 * To create the table for this:

   CREATE TABLE `firewall` (
   `ip` int(11) NOT NULL default '0',
     PRIMARY KEY  (`ip`)
     ) ENGINE=MyISAM DEFAULT CHARSET=binary COMMENT='ip addresses to block';
 */
class Firewall {

    /**
     * Adds an IP address to the table.
     */
    public static function add($ip) {
        $db_obj = new DB;
        $ipint = ip2long($ip);
        if ($ipint) {
            $db_obj->execute_statement("INSERT INTO firewall VALUES(".$ipint.")");
        }
    }

    /**
     * Checks if an address is blocked.
     */
    public static function isBlocked($ip) {
        $db_obj = new DB;
        $ipint = ip2long($ip);
        if ($ipint) {
            $result = $db_obj->query("SELECT COUNT(*) AS ct FROM firewall WHERE ip=".$ipint);
            return (intval($result[0]['ct'])==1);
        }
        return false;
    }

    /**
     * Checks if the client's IP address is blocked,
     * and exits the script if it is, sending a 500 status code.
     *
     * This is the most common use case for this class.
     */
    public static function protect() {
        if (Firewall::isBlocked($_SERVER['REMOTE_ADDR'])) {
            http_response_code(500);
            exit();
        }
    }
}

To use it, you include this file, and then use Firewall::add($_SERVER['REMOTE_ADDR']) to add addresses.

You use Firewall::protect() to block. That will just exit the script immediately. You should put your protection line near the top of specific pages you want to protect.

The calls to Firewall::add() may be deeper in your application, somewhere before or after your input validation.

This isn’t a general-purpose firewall. It’s a library so you can write some code to detect specific attacks, and then stick it into your legacy application and protect yourself. It’s a hack, and I’m sharing it so you won’t have to write so much code.

Some Attacks Described

Typically, the attacker has a script running across a bot network, so you get multiple IP addresses coming at you, but the addresses usually repeat a little bit.

The attacks are usually SQL injection attacks, which contain some SQL code. It’ll look like this: ?id=1234%20’%20OR%201=1%20UNION…

Sometimes, they are trying to read a configuration file, by using the “../” path to climb up into a different directory: ?file=../../../../settings/config.php. This is a path traversal attack.

Sometimes, they are attempting to upload a script. The script will start with something like a JPEG header, and then in the middle of it there’s a PHP tag. The file also contains “.php” in the name. This is a double extension attack: the webserver tries to identify the file by popping extensions off the end of the file.

Sometimes, the uploads will have filenames starting with pipe “|”, and that can be used to inject commands. Sometimes, the hack will be against the .htaccess file, with new settings in the file that will cause the file to be executed like a script.

Sometimes, they are exploiting holes in image libraries, compression libraries, or PHP extensions. These are a lot harder to detect.

They will also try to brute-force the admin screens, which is a little harder to detect, unless they keep hitting the same page over and over with nonexistent names.

Leave a Reply