Stephen Balkum

Agile Enthusiast, Leader, and Developer

Restricting downloads in WordPress on Lighttpd

no comment

Here’s the scenario: you have a WordPress site with a collection of files that need to be protected to only authenticated users.  The site is not super high security, but you want a basic obstacle.

All of your searches will talk about .htaccess file assuming you are running Apache.  But, you have chosen a leaner server running Lighttpd which does not support .htaccess files.

I installed the Media File Manager plugin to easily create child folders in the uploads folder and be able to move files around.  My goal is for non-techies to maintain this site.  Using this plugin, I created the members-only folder inside uploads.  Inside this folder is this php file:

<?php
if( !empty( $_GET['name'] ) )
{
  // check if user is logged
  if( is_logged_in() )
  {
    $clean_name = str_replace("..", "", $_GET['name']);
    $clean_name = str_replace("/", "", $clean_name);
    $filename = "{$_SERVER['DOCUMENT_ROOT']}/wp-content/uploads/members-only/{$clean_name}";
    if( file_exists( $filename ) )
    {
      header( 'Cache-Control: public' );
      header( "Content-Disposition: attachment; filename={$clean_name}" );
      if(!empty($_GET['type']))
        header( "Content-Type: {$_GET['type']}");
      readfile( $filename );
      exit;
    }
  }
}
die( "ERROR: invalid file or you don't have permissions to download it." );

function is_logged_in() {
  foreach ($_COOKIE as $cookie_name => $cookie_value) {
    if(startswith($cookie_name, "wordpress_logged_in_"))
      return true;
  }
}

function startswith($haystack, $needle) {
  return substr($haystack, 0, strlen($needle)) === $needle;
}
?>

To block a url direct to the file, I added a new rewrite rule to the top of the list:

url.rewrite-final = (
  "^/wp-content/uploads/members-only/(.*)" => "/wp-content/uploads/members-only/download.php?name=$1",
  # Exclude some directories from rewriting
  "^/(wp-admin|wp-includes|wp-content|gallery2)/(.*)" => "$0",
  # Exclude .php files at root from rewriting
  "^/(.*.php)" => "$0",
  # Handle permalinks and feeds
  "^/(.*)$" => "/index.php/$1"

At this point, I can put links in my posts and have a modicum of security from users passing around the links to unauthorized users.  I can even specify the content-type:

http://www.mysite.com/wp-content/uploads/members-only/newsletter.pdf&type=application/pdf

Hope this helps.