I am trying to secure my PHP Image upload script and the last hurdle I have to jump is making it so that users cannot directly excecute the images, but the server can still serve them in web pages. I tried changing ownership and permissions of the folders to no avail, so I am trying to store the images above public_html and display them in pages that are stored in public_html.
My File Structure:
- userimages
image.jpg
image2.jpg
- public_html
filetoserveimage.html
I tried linking to an image in the userimages folder like this:
<img src="../userimages/image.jpg">
But it does not work. Is there something I am missing here? If you have any better suggestions please let me know. I am trying to keep public users from executing potentially dangerous files they may have uploaded. Just as an extra security measure. Thanks!
You want something that's basically impossible.
The way a browser loads a page (in a very basic sense) is this:
Step 1: Download the page.
Step 2: Parse the page.
Step 3: Download anything referenced in the content of the page (images, stylesheets, javascripts, etc)
Each "Download" event is atomic.
It seems like you want to only serve images to people who have just downloaded a page that references those images.
As PHP Jedi illustrated, you can pass the files through PHP. You could expand on his code, and check the HTTP_REFERER on the request to ensure that people aren't grabbing "just" the image.
Now, serving every image through a PHP passthru script is not efficient, but it could work.
The most common reason people want to do this is to avoid "hotlinking" -- when people create image tags on other sites that reference the image on your server. When they do that, you expend resources handling requests that get presented on someone else's page.
If that's what you're really trying to avoid, you can use mod_rewrite to check the referer.
A decent-looking discussion of hotlinking/anti-hotlinking can be found here
Use an image relay script!
To serve a imagefile that is outside the public_html folder you would have to do it by a php script. E.g make a image-relay.php that reads the image that is outside the public html...
<?php
header('Content-Type: image/jpeg');
$_file = 'myimage.jpg'; // or $_GET['img']
echo file_get_contents('/myimages/'.$_file);
?>
Now, $_file could be a $_GET parameter, but its absolutley important to validate the input parameter...
now you can make an <img src="image-relay.php?img=flower.jpg">
to access a flower.jpg image that is located in /myimage/flower.jpg ...
Well, a web browser will only be able to access files and folders inside public_html.
If the public_html
directory is the root of the server for your users, Apache cannot serve anything that is not inside/below that dorectory.
If you want a file to be served by Apache directly, you'll have to put it in/below public_html
.
I think your misunderstanding is in the fact that if you include an image in an <img>
tag, your browser will send the exact same request to the webserver to fetch it, that will be sent to the webserver if you try to open the src
url of the image in your browser directly.
Therefore, either both things work, or neither.
There are hacks around, involving a (php or other) script to make sure that an IP that has requested the image has also requested the html page within the last few seconds (which will not work if the user is behind a proxy that rotates outgoing IPs) or by checking the referer (which does not work with HTTPs and also not if the user has referer disabled).
If you want to make sure that only some users can see the image (both via <img>
tag and directly), you can put the image outside public_html and have a (php or other) script that verifies the user's credentials before serving the image.
If you are using Apache or lighttpd you can use the X-Sendfile header to send files that are not in the web root(provided you haven't changed the configuration of mod_xsendfile).
To learn more about X-sendfile see this site.
This solution is giving you the best possible performance as PHP doesn't send the file but the server does and therefore PHP can be exited while the files are being served.
Hope that helps.