More control over baddies: PHPIDS and WordPress

A few weeks ago I stumbled on the PHPIDS project. (Most likely from PenTestIT) It seemed like a pretty cool idea, I mean who doesn't love more logs? And I've been experiencing quite a bit of spam messages despite my attempts to add spam catching plugins and CAPTCHAs; though I've tried to maintain usability by not requiring registration. So perhaps PHPIDS can me understand my baddies a bit better.

Their documentation is simple and concise, head to their FAQ page to get up and running in seconds (assuming you've already installed the library.) I found an article on The H Security website which also helped. I had found their article by searching for implementations into WordPress. Updates to the code described in the article can be found in the PHPIDS examples directory.

My primary goal was it integrate the library into, and test using, WordPress. My secondary goal was to install PHPIDS such that any of my web applications could make use of the library.

Installing PHPIDS

I unpacked PHPIDS into /usr/local/share. The only parts relevant to PHP are the library files located in the IDS folder within the unpacked PHPIDS. I would suggest installing the files into your web directory if plan on using it for a specific web application only. (Don't bother junking up your filesystem.) I'm working on a play-around machine so I'll junk all I want! I then linked /usr/share/php5/IDS to /usr/local/share/phpids/lib/IDS. Since /usr/share/php5 is part of my default include path for php, I can eliminate some flack from the setup file used by PHPIDS.

Shorthand:

  • Unpack PHPIDS to /usr/local/share/phpids
  • Symlink /usr/share/php5/IDS to /usr/local/share/phpids/lib/IDS

Creating a PHPIDS Setup

The 'setup' file (this is my term) is used as a quasi-configuration file as it tells PHPIDS how to act for the application that's including it. As such, this file (I call it ids.php) should be included into some header file for the web application utilizing the IDS. Within the setup file are instructions on how to log alerts, where to find the PHPIDS library, and what request variables to scan.

I'm not into any fancy-pants logging. For now I just want alerts dumped to a text file. I'll let my various log alerting processes scrape and rotate the file. Here's my simple no-frills setup file (using PHPIDS version 0.6.4):

<?php
/**
* PHPIDS
* Copyright (c) 2010 PHPIDS group (http://php-ids.org)
*/

if (!session_id()) {
  session_start();
}

require_once 'IDS/Init.php';

try {
  $request = array('GET' => $_GET, 'POST' => $_POST, 'COOKIE' => $_COOKIE);
  $init = IDS_Init::init('/usr/share/php5/IDS/Config/Config.ini.php');

  $init->config['General']['base_path'] = '/usr/share/php5/IDS/';
  $init->config['General']['use_base_path'] = true;
  $init->config['Caching']['caching'] = 'none';

  $ids = new IDS_Monitor($request, $init);
  $result = $ids->run();

  if (!$result->isEmpty()) {
    require_once 'IDS/Log/File.php';
    require_once 'IDS/Log/Composite.php';
    $compositeLog = new IDS_Log_Composite();
    $compositeLog->addLogger(IDS_Log_File::getInstance($init));
    $compositeLog->execute($result);
  }
} catch (Exception $e) {
  /* Do nothing for now */
}
?>

The log and cache path are set in the PHPIDS configuration file. Using my setup this is located in /usr/local/share/phpids/lib/IDS/Config/. This configuration file also lets you specify special request fields that may contain HTML. It also let's you define exceptions. I wouldn't recommend using exceptions but I found that a few tracking variables (used by Google Analytics) create a ton of false positives. I haven't been using PHPIDS for very long but here's my setup:

html[]          = POST.__wysiwyg
html[]          = POST.content
; define which fields contain JSON data and should be treated as such
; for fewer false positives (new in PHPIDS 0.5.3)
json[]          = POST.__jsondata

; define which fields shouldn't be monitored (a[b]=c should be referenced via a.b)
exceptions[]    = GET.__utmz
exceptions[]    = GET.__utmc
exceptions[]    = COOKIE.__utmz
exceptions[]    = COOKIE.__utmc

Make sure the POST.content is included under HTML if you plan on using WordPress. As new posts, pages, and comments all use this field and can except HTML. Nice functionality, might need some tweaking as I collect more false positives.

Configuring WordPress

I choose to store my sample file (ids.php) in /var/www. All my web applications have sub folders within www organized by their name, then another set of folders organized by function. I then opened wp-config.php and added an include for ids.php at the bottom. After a few days I decided to exclude intrusion monitoring from WordPress administrators as the number of false positives was outrageous. Here's what I have included now:

if (!current_user_can('manage_options')) {
  require_once('/var/www/ids.php');
}

Simple.

Moving Forward

As of now it's picking up very basic (and failed) attempts of XSS, not tailored to WordPress, as well as interesting registration attempts. For now I'm also keeping Apache Access Logs so I can measure the strength of PHPIDS and hunt for false negatives. I'll be sure to update this post if I find anything significant.

As a parting note, I would not configure PHPIDS to email alerts as those emails will be sent inline with Apache serving content which will hinder the user experience. If you'd like to have email alerts I would recommend polling a log file from cron or delivering alerts to syslog and creating a rule using swatch.

There's also a new project aimed at integrating IDS into WordPress called WP-IDS, you can find the project here. It is in infancy but definitely looks promising.