Open source software security

Defending Web Applications with PHPIDS

30 November -0001

PHPIDS (http://php-ids.org) is a very intriguing project that mimics the functionality of much more involved intrusion detection systems. PHPIDS is written entirely in PHP, so it should be supported by almost any platform that supports PHP applications, although PHP version 5.1.2 or greater is required. PHPIDS also requires SimpleXML support and PDO in order to facilitate database interaction. Because PHPIDS is written in the same language as applications it is designed to defend installation is eased considerably. Chances are that if your PHP application is running you can install PHPIDS.

Overview of PHPIDS

In a nutshell, PHPIDS is a library of PHP code that is inserted in every PHP page of a new or existing application. The library inspects all the input parameters being passed to the page and compares them to a filter file. This file does pattern based recognition to determine if input is potentially malicious, calculating a score based on PHPIDS rules. Developers can then use this calculation to carry out any of three predefined actions or to alter program flow if they so choose.

PHPIDS has three native reporting engines. The first is to log to a file stored on the filesystem. Each log entry is a list of comma separated, double quoted values, making the log fairly easy to read and parse. The second reporting mechanism is via e-mail. This causes an e-mail to be sent to recipients defined in the PHPIDS configuration file. (Un)fortunately much of the data, such as malicious URL's is url encoded for safety, which makes them a little difficult to read in an email client. The third method of reporting is via database. PHPIDS requires a simple table in a database in which it records it's results.

PHPIDS will report severity level of an event, description, the source IP of the event, the URL upon which the event was detected, and the suspicious payload that triggered the event. This information is sparse but handy. The severity score, or "impact" is a calculation based on regular expression matching defined in PHPIDS' XML based filter file. PHPIDS comes with a default_filter.xml prepackaged with the application. Various XML filters are compared to input strings, and each filter has an associated impact. Reports include the filter tags (such as xss, csrf, etc.) for which PHPIDS detected hits along with the calculated impact.

Installation

Installing PHPIDS is relatively straightforward. The package can be downloaded in multiple formats. Once downloaded you'll have to inflate the source. This will usually result in a directory called 'phpids-' followed by the version information (such as 'phpids-0.6'). Once you've downloaded and extracted the package you'll need to copy the lib/IDS directory out of the phpids-0.6 directory and into the root of your web application. At this point you'll need to customize the config file in IDS/Config/Config.ini. Be sure to add an .htaccess file to the Config directory to deny access!!! You'll want to define paths, database connection information, email addresses, etc. in this configuration file. Once this is done you need to create a temporary directory called 'tmp' (as defined in the Config.ini) where PHPIDS can write information (so your web server will need write access). You can do this using:

$ cd IDS
$ mkdir tmp
$ sudo chgrp apache tmp
$ chmod g+w tmp

Once this is complete PHPIDS should be ready to go. If you want database support you'll have to fill in the Config.ini database connection information and create the database and table for PHPIDS. You can create the database and table using the model from IDS/Log/Database.php:

    CREATE DATABASE IF NOT EXISTS `phpids` DEFAULT CHARACTER 
        SET utf8 COLLATE utf8_general_ci;
    DROP TABLE IF EXISTS `intrusions`;
    CREATE TABLE IF NOT EXISTS `intrusions` (
      `id` int(11) unsigned NOT null auto_increment,
      `name` varchar(128) NOT null,
      `value` text NOT null,
      `page` varchar(255) NOT null,
      `ip` varchar(15) NOT null,
      `impact` int(11) unsigned NOT null,
      `origin` varchar(15) NOT null,
      `created` datetime NOT null,
      PRIMARY KEY  (`id`)
    ) ENGINE=MyISAM ;

The last step is to simply implement PHPIDS on your pages. If you're using tight MVC architecture this may be relatively few files. In any case, you'll want to add code such as the following to each page you'd like monitored by PHPIDS:

<?php
  require_once 'IDS/Init.php';
  $request = array(
      'REQUEST' => $_REQUEST,
      'GET' => $_GET,
      'POST' => $_POST,
      'COOKIE' => $_COOKIE
  );
  $init = IDS_Init::init('IDS/Config/Config.ini');
  $ids = new IDS_Monitor($request, $init);
  $result = $ids->run();

  if (!$result->isEmpty()) {
   // Take a look at the result object
   		/*
        * The following steps are optional to log the results
        */
        require_once 'IDS/Log/File.php';
        require_once 'IDS/Log/Composite.php';

        $compositeLog = new IDS_Log_Composite();
        $compositeLog->addLogger(IDS_Log_File::getInstance($init));

        /*
        * Note that you might also use different logging facilities
        * such as IDS_Log_Email or IDS_Log_Database
        *
        */
        
        require_once 'IDS/Log/Email.php';
        require_once 'IDS/Log/Database.php';

        $compositeLog->addLogger(
            IDS_Log_Email::getInstance($init),
            /DS_Log_Database::getInstance($init)
        );
        
        $compositeLog->execute($result);
  }

  ?>

Adding this code will cause alerts to be emailed to the Config.ini defined recipients, log entries to be written and a database entry to be recorded as well. This allows pretty robust coverage in detecting alerts. For instance, if you have this code on an index.php page that doesn't do anything and you call that page using with the URL "/index.php?foo=../../../../../../etc/passwd" you'll get the following entry in your log file:

"192.168.0.50",2009-05-20T14:49:56-04:00,44,"dt id lfi xss csrf rfe","REQUEST.foo=..%2F..%2F..%2F..%2F..%2F..%2Fetc%2Fpasswd GET.foo=..%2F..%2F..%2F..%2F..%2F..%2Fetc%2Fpasswd","%2Ftestapp%2Findex.php%3Ffoo%3D..%2F..%2F..%2F..%2F..%2F..%2Fetc%2Fpasswd","192.168.0.2"

And a similar e-mail that will look like:

Subject:  PHPIDS detected an intrusion attempt!

The following attack has been detected by PHPIDS

IP: 192.168.0.50 
Date: 2009-05-20T14:49:03-04:00 
Impact: 44 
Affected tags: dt id lfi xss csrf rfe 
Affected parameters: REQUEST.foo=..%2F..%2F..%2F..%2F..%2F..%2Fetc%2Fpasswd, GET.foo=..%2F..%2F..%2F..%2F..%2F..%2Fetc%2Fpasswd, 
Request URI: %2Ftestapp%2Findex.php%3Ffoo%3D..%2F..%2F..%2F..%2F..%2F..%2Fetc%2Fpasswd 
Origin: 192.168.0.2

As you can see the impact is noted, along with the offending details. Note that IP addresses will vary on your own installation.

Conclusions

PHPIDS is a pretty good, lightweight intrusion detection system. The fact that it does calculations on the fly gives developers a lot more control over the security of their web applications. Rather than having to rely on a web application firewall or host based intrusion detection to mitigate these threats, application developers can use PHPIDS to dynamically craft a programmatic response to attack behavior. This could be as simple as presenting the user with a warning or as aggressive as blacklisting an offending IP. What's even nicer is that this can be done on a per-application basis. By installing PHPIDS centrally and using PHP's library include directives you could make PHPIDS available to all applications on a site without having to worry about having each application maintain a copy of the code.

I would say that the only downfall to PHPIDS is the lack of a reporting interface. Email alerts are nice, but e-mail isn't very good for doing metrics, sorting, or other complex analysis. The log file and database back end present some great data repositories, but there isn't any GUI or command line reporting tool that makes the data accessible to end users. Perhaps this could be a feature of a later release. It does however free developers to include reporting in their own security event management systems.

All in all PHPIDS is a win for most organizations. It's low overhead and default passive nature make it great for putting additional information into security professionals' hands. PHPIDS is easy to configure and should be easily implementable by any skilled PHP developer. I'll be very interested in observing the maturation of this project.