1

Hope your day is well.

I am currently trying to serve images with PHP that is not in the document root. This needs to be account specific so only specific users can access their images.

I am looking into X-Sendfile, but don't know if there's a better way. And so far I haven't got X-Sendfile to work.

I essentially need to be able to load an image so it can be referenced by HTML,

<img src="image.png" alt="">

Like that. So basically loading a file as image.png and then referencing it in HTML. I don't know a better way to do this if there is one, and how to do it all. Please help me.

Have a good day.

Chris
  • 154
  • 8

1 Answers1

0

Generally what you want to do is:

Determine in what sort of way you can uniquely identify an image - in a way that doesn't leak information. A primary key so to speak. If the metadata for your images happens to be stored in a database, then each image likely already has a unique key (the primary key in that table; perhaps an auto_incrementing integer). It could be a string as well (or a UUID); it just has to be something you'd have no problem with exposing to the world (eg: no proper filenames like birthday.jpg, no user_john.png; ideally random values, but sequential ones (like mysql SERIALs) are probably tolerable enough.


Create a new "empty" page or route, lets call it say /image.php


Now, in your original page (not the new /image.php yet, but the one you already had), you'll query the database to determine which images you want to show, and you use the unique id for those images to output something like

printf('<img src="/image.php?id=%s" alt="">', $imageID);

That <img> tag will cause the user to automatically make an additional request to that new /image.php page, in order to download the image.


Inside /image.php , you'll take the requested ID from $_GET['id']. then you'll query the database to see if that is an image this person should be able to access (and perhaps also determine what the filename is, on the server). lets say your queries determined that:

$allowAccess = true;
$filename    = '../imgstorage/756345343465r563523.png';

Now you just make php send out some headers to identify the upcoming data as being an image, and then just pass the file through:

if ($allowAccess && file_exists($filename)) {
    Header("Content-Type: image/png");
    Header("Content-Length: " . filesize($filename));
    readfile($filename);
    Exit(0);
} else die('Go away');

and that's all there's to it.


The files can be located anywhere; both inside or outside the webroot (makes no difference), but PHP does need to have (filesystem)permissions to read from those files of course.

Raxi
  • 2,452
  • 1
  • 6
  • 10
  • Thank you so much! Very informative. I have one more question though. Would it be possible to serve multiple images from one PHP page? – Chris Dec 14 '21 at 12:41
  • I thought of something. I could do a ?GET request and serve specific images on that. That is one way. I'd be interested to know if you could serve 2 images with different filenames from one page sort of. If not I'll stick to a GET or POST request. – Chris Dec 14 '21 at 12:44
  • Yup, add as many as you like, another `printf('', $imageID);` for each additional image. – Raxi Dec 14 '21 at 12:44
  • yea you could combine the files in the way you describe, nothing wrong with that; but in either case each `` will result in an extra request; so if the page has 4 images, you'll get 5 hits (first hit to your `currentpage.php` and after that 1x to `/image.php` each) – Raxi Dec 14 '21 at 12:47
  • **if you could serve 2 images with different filenames from one page sort of** -> yes, thats why the unique identifier is important, so `image.php` knows which image to serve. – Raxi Dec 14 '21 at 12:49
  • Alright. Thanks so much! – Chris Dec 14 '21 at 13:15
  • No problem, Good luck – Raxi Dec 14 '21 at 13:35