1

I have all my images saved in the root directory where the normal user can't access. I randomly generate all the filenames, so I don't want them to know the path or name of the file. Currently, I have the img element as so:

<img src="get_image.php?id=1">

I have my get_image.php page as so:

<?php
include_once 'includes/db_connect.php';
include_once 'includes/functions.php';

$uploaddir = '/root/......./uploads/'; //Direct path from root to folder holding files
$id = $_GET['id'];

if(!is_numeric($id)) {
    die("File id must be numeric");
}

// A QUICK QUERY ON A FAKE USER TABLE
$stmt = $mysqli->prepare("SELECT `name`, `mime` FROM `uploads` WHERE `filmId`=?");
$stmt->bind_param("i", $id); 
$stmt->execute();

$stmt->bind_result($name, $mime);

// GOING THROUGH THE DATA
while($stmt->fetch()) {

    header("Content-Type: " . $mime);
    readfile($uploaddir.$name);
}

?>

If there someway I can do this? I don't want to call the image file src directly like so:

<img src="../../../uploads/file.jpg"> <!-- I don't want to do this -->

Doing it that way will give the user (or attacker) a hint of where I keep my uploaded files. These images are user-uploaded, so if an attacker were to upload an image with malicious code (even though I have lots of code checking and validating the file), they wouldn't know where that file was saved.

Also, I'm new to web development and keeping it secure. If you have any hints or pointer to add onto what I have to make my webpage more secure, please feel free. Thanks!

I'm using this link to make this functionality secure.

SOLUTION:

Instead of:

    header("Content-Type: " . $mime);
    readfile($uploaddir.$name);

I put this:

    header("Content-Type: " . $mime);
    $contents = file_get_contents($uploaddir . $name);
    echo $contents;

and it works.

Rob Avery IV
  • 3,562
  • 10
  • 48
  • 72
  • 2
    Why exposing a file path is a security issue? – rpax Jun 18 '14 at 16:25
  • @rpax I guess if somehow they were able to get access to the root directory, they would exactly where to go. Not to mention that I give my images random names, so having it "hard coded" like that could help them know which file is which. I'm just paranoid and want my site to be as secure as possible. – Rob Avery IV Jun 18 '14 at 16:28
  • 3
    so what's to stop someone from trying `get_image?id=1` through `get_image?id=99999999` and grabbing ALL of your images anyways? You're gaining nothing in the way of "security". At most you've prevented them from knowing what the image's "real" name is, so that `id=1` -> `boobies.jpg` and `id=50` -> `kittens.jpg`. – Marc B Jun 18 '14 at 16:33
  • You can create another directory, with a virtual path. something like `/images/image.jpg` Take a look at [mod_alias](http://httpd.apache.org/docs/2.4/mod/mod_alias.html#alias) – rpax Jun 18 '14 at 16:33
  • @MarcB with `get_image`, you won't see the file name. Like I said, all my filenames are randomly generated. I don't care if they save the file. I just don't want them to see the actual filename. – Rob Avery IV Jun 18 '14 at 16:36
  • 1
    @RobAveryIV what is the problem with your current code? See http://stackoverflow.com/questions/1851849/output-an-image-in-php. That aside, there is no security benefit in hiding the image name. If atacker manages to upload malicious code, how is that helpful to know where it is stored if he cannot execute it? And if he can execute code on your system, why would he bother with finding out 'real' image path? – ya23 Jun 18 '14 at 16:50
  • If an attacker gets access to your root directory, they can just save and execute the file directly, and ignore your image upload system. You're putting too much effort into playing at security instead of putting that effort towards learning how to actually secure your system. There's some value in not allowing an attacker to access a file they just uploaded, but there's no value in hiding the file name or location. You can spend your time being paranoid, or you can spend your time educating yourself and doing the actual right things. – Jason Jun 18 '14 at 17:02
  • @MarcB - Got to chuckle at what must have been going through your head to come up with boobies and kittens for an example :) – webnoob Jun 18 '14 at 20:49
  • 1
    @webnoob: what else is the internet for? – Marc B Jun 18 '14 at 21:08
  • @RobAveryIV if one of the answers was satisfactory, please click the checkmark on the left. – David Jun 21 '14 at 12:42

4 Answers4

1

Not only that you can do that. You can even point a <img> to a PHP script that seems to be image path. Eg.:

<img src="phpimages/my_image.png" />

This trick needs an Apache HTTP server (which you undoubtedly use) and mod_rewrite plug-in for it (which you sure have installed). You can than put a .htaccess file in /phpimages/ with such contents:

RewriteRule ^([^\.]+)\.(png|jpg|gif)$   /image.php?file=$1.png [NC,L]

In case you just want user IDs, you can limit the regular expression to:

RewriteRule ^([0-9]+)\.(png|jpg|gif)$   /image.php?file=$1.png [NC,L]

This process is further explained here.

In script image.php (in the same directory), you then get filename as string, eg.:

$filename = $_GET["file"];  //eg.: "file1.jpg"

Don't forget to filter out ../ hack attempts. To print the file to the user, provided it really exists, use readfile:

const REAL_AVATAR_PATH = "../../../uploads/"
if(file_exists(REAL_AVATAR_PATH.$username)) {
     header("Content-Type: image/jpg");  //For simplicity, I have ommited to send different header for different images as "png", "gif" and so on.
     readfile(REAL_AVATAR_PATH.$username);
}
else {
     header("HTTP/1.0 404 Not Found");
     die("Error 404 - sorry");
}

In your case, you could have path as /avatars/[number].png (and have all the images converted to png).

Community
  • 1
  • 1
Tomáš Zato
  • 50,171
  • 52
  • 268
  • 778
1

Instead of:

header("Content-Type: " . $mime);
readfile($uploaddir.$name);

I put this:

header("Content-Type: " . $mime);
$contents = file_get_contents($uploaddir . $name);
echo $contents;

and it works.

Rob Avery IV
  • 3,562
  • 10
  • 48
  • 72
0

I have to disagree with the current answers. If you're looking through a database, then you could easily do something such as:

<img src="/phpimages.php?id=23" alt="{ALT}" />

phpimages.php would then take ID 23 and output the file. Your end users would have no clue where the images were coming from; they could even come from another server. This option also allows for a bit more readable code, while still maintaining the same security.

<img src="/phpimages.php?frontImage" alt="{ALT}" />

Now you just use something like described in the link below which would allow you to get the key of the first $_GET association. Or you could make it id=frontImage and just use $_GET['id']

Get first key in a (possibly) associative array?

Community
  • 1
  • 1
David
  • 4,744
  • 5
  • 33
  • 64
  • 1
    That's just added value to the basic answer. The real reply would be, since you *dissagree* - why not? What is the wrong thing with that? – Tomáš Zato Jun 18 '14 at 20:59
-1

In PHP file you need to set your header Content Type as

 header('Content-Type: image/png');

So that the browser get to know that get_image.php gives an image as response

Let the path of your temporary image is stored in $path

<?php
$im = imagecreatefrompng($path);

header('Content-Type: image/png');

imagepng($im);
imagedestroy($im);
?>
Abhishek Batra
  • 1,539
  • 1
  • 18
  • 45
  • 5
    Wait, why are you reading in and reencoding the image? There's absolutely no reason to round-trip through GD here; just use `readfile()` if you want to pass it through! –  Jun 18 '14 at 17:45