WordPress, PHP, and Shared Hosting

With WordPress more popular than ever, boutique hosts are popping up everywhere offering the promise of “bulletproof” hosting. For those that don’t have the resources to administer a VPS, a managed environment tuned for performance, security, and reliability seems worth paying the extra expense. After vetting three of these services for a project, my advice is “Be careful.” Not all services are created equal.

The Problem

Shared PHP hosting requires a careful system admin. In a typical mod_php Apache httpd installation, all virtual hosts share the same Apache instance running with the same permissions. Unless special precautions have been taken, any PHP file can read/write/execute any file that the Apache process has permission to read/write/execute. Deploying in this situation means that you must trust all of your neighbors.

Over the last week, I tested three managed WordPress hosts. How did they fare? Not, good. Two hosts with a good reputation in the community have serious vulnerabilities. The vulnerabilities are severe enough that I could easily manipulate the data of an adjacent installation.

How do you know if your host is vulnerable?

Below is a script borrowed from phpsec.org:

<?php

echo "<h3>Current directory:" . dirname(__FILE__) . "</h3>\n";

echo "<pre>\n";

if (ini_get('safe_mode'))
{
    echo "[safe_mode enabled]\n\n";
}
else
{
    echo "[safe_mode disabled]\n\n";
}

if (isset($_GET['dir']))
{
    echo "<h4>Scanning: " . htmlentities($_GET['dir']) . " </h4>\n";
    ls($_GET['dir']);
}
elseif (isset($_GET['file']))
{
    cat($_GET['file']);
}
else
{
    echo "<h4>Scanning: / </h4>\n";
    ls('/');
}

echo "</pre>\n";

function ls($dir)
{
    $handle = dir($dir);

    while ($filename = $handle->read())
    {
        $size = filesize("$dir$filename");

        if (is_dir("$dir$filename"))
        {
            if (is_readable("$dir$filename"))
            {
                $line = str_pad($size, 15);
                $line .= "<a href=\"{$_SERVER['PHP_SELF']}?dir=$dir$filename/\">$filename/</a>";
		if(is_writable("$dir$filename"))
		{
			$line .= " (writable)";
		}
            }
            else
            {
                $line = str_pad($size, 15);
                $line .= "$filename/";
            }
        }
        else
        {
            if (is_readable("$dir$filename"))
            {
                $line = str_pad($size, 15);
                $line .= "<a href=\"{$_SERVER['PHP_SELF']}?file=$dir$filename\">$filename</a>";
		if(is_writable("$dir$filename"))
		{
			$line .= " (writable)";
		}

            }
            else
            {
                $line = str_pad($size, 15);
                $line .= $filename;
            }
        }

        echo "$line\n";
    }

    $handle->close();
}

function cat($file)
{
    ob_start();
    readfile($file);
    $contents = ob_get_contents();
    ob_clean();
    echo htmlentities($contents);

    return true;
}

?>
  1. Copy the code to your favorite editor, save as dir-scan.php, and upload to the root of your web directory.
  2. Visit the page by going to http://hostname.com/dir-scan.php
  3. When the page loads, you will see the current directory, whether safe_mode is active, and listing of the files/directories in “/”. If no files are listed that is good.
  4. To view a specific directory, go to http://hostname.com/dir-scan.php?dir=/var/www/path.

Subversion, Vendor Branches, and svn_load_dirs.pl

Yesterday, I started work on a project to upgrade the BU CMS to version 3.1.x of WordPress. We follow the vendor branch setup for handling vendor drops as suggested in the Subversion book. Unfortunately, Mac OS X Snow Leopard (10.6.6) does not ship with the additional subversion tools installed, specifically svn_load_dirs.pl. After doing a bit of hunting, I came across a support document for Drupal that pointed me in the right direction.

1. Download the source code for svn_load_dirs.

svn co http://svn.apache.org/repos/asf/subversion/tags/1.6.6/contrib/client-side/svn_load_dirs svn_load_dirs

2. Move svn_load_dirs.pl.in to a bin directory and rename to svn_load_dirs.pl.

mv svn_load_dirs/svn_load_dirs.pl.in ~/bin/svn_load_dirs.pl

3. Because we will not be building subversion from source, we need to edit the script and replace

my $svn = '@SVN_BINDIR@/svn';

with

 my $svn = '/usr/bin/svn';