BurnerBoy
BurnerBoy

Reputation: 93

PHP - Security in accessing files outside of web root

I have a php file called gallery.php which is the page for a user specific gallery of images. For security, the user images are stored outside of web root.

To retrieve the appropriate files for each user, I use a getimage.php file which serves the images from their location. All in all, the directory structure looks like this:

getimage.php is written as follows:

$imgString = realpath('/UserImages/' . $_SESSION['username'] . '/' . $_GET['img']);
if (!startsWith($imgString, '/UserImages/' . $_SESSION['username'] . '/')
    || !(endsWith(strtolower($imgString), '.jpg') || endsWith(strtolower($imgString), '.jpeg')))
{
    header('HTTP/1.0 403 Forbidden');
    die();
}

$img = file_get_contents($imgString);
header("Content-type: image/jpeg");
echo($img);
exit();

I rely on $_GET['img'] to determine which image to retrieve, which is a possible security hole (and a major one at that). I could forsee a directory traversal attack, hence the use of realpath, though I'm sure there can be other avenues of attack I do not contain with this script.

For that reason, I'd prefer if I could move getimage.php outside of webroot, or at least prevent it from being accessible directly (and only through gallery.php, where the sent img parameter is strictly under my control).

Any time I try to move getimage.php oustide of public_html however, I can't seem to call it anymore even if I do a require or include in gallery.php. The way I access getimage.php is doing this:

<img src=getimage.php?img=IMG_FILENAME.jpg />

But getimage.php will fail if I ever move it out of the public_html directory.

So, long story short: what do I need to do to prevent getimage.php from being abused?

Upvotes: 4

Views: 5368

Answers (3)

DeepBlue
DeepBlue

Reputation: 694

The best secure way is to not pass image url using $_GET['img'], but send reference to the image instead.

The Problem

Firstly, $_GET['img'] can be any external url to a harmful code, and with file_get_contents you are downloading it directly.

Secondly, anyone can use your server as free image proxy by simply using the following url in his own website:

<img src="http://www.yourwebsite.com/getimage.php?img=[external_image_url]">

The Correct Way

1) In your database create images table containing paths to user images. When you want to serve an image pass the image ID instead of the url:

$imageID = $_GET['image'];
.... your sql query here to retrieve the image....
$img = file_get_contents($imgString);

2) Because you know that $_GET['image'] is an ID you can sanitize it easily:

if( !preg_match("/^[0-9]+$/", $_GET['image']) )
{
    header('HTTP/1.1 404 Not Found');
    exit();    
}

3) In your SQL dont forget to use prepared statments:

$sql = "SELECT image_path from images WHERE id = ?";

4) Only images should be moved outside document root folder, if you want to move getimage.php outside root as well you can use Apache Alias to serve it. In Apache Virtualhost config you can use:

Alias /getimage.php /path/to/getimage.php

5) Use 404 not found instead of 403 forbidden, don't give the attacker a hint that you caught his action.

Upvotes: 3

RCrowt
RCrowt

Reputation: 961

The only way to do this using your directory structure is to clean the $_GET['img'] variable and ensure it doesn't have any unwanted characters.

You could do this with a regular expression which filters everything except Letters, number, underscores ,hyphens and dots.

Try replacing the first line of your code with this...

$imgString = realpath('/UserImages/' . $_SESSION['username'] . '/' . preg_replace("/[^a-zA-Z0-9._-]/", "", $_GET['img']));

Alternatively you could check to see if the $_GET['img'] variable contains bad characters with this simple If statement before your code.

if(preg_replace("/[^a-zA-Z0-9._-]/", "", $_GET['img']) != $_GET['img']){
    // Throw an error
    exit();
}

PS: don't forget to check your file exists before reading it or you will get errors if it doesn't. You could use the files existence as an additional security check.

Upvotes: 1

Dukecz
Dukecz

Reputation: 362

You need to allow Apache (assumed) to access that folder with images using Directory directive (http://httpd.apache.org/docs/2.2/mod/core.html#directory).

Or you can move images under root directory and restrict direct access to them using .htaccess if you want to keep them protected.

Upvotes: 1

Related Questions