1

I am currently trying to develop an image uploading website by using CodeIgniter. The thing is, I came across an issue today and I would really appreciate any kind of help in order to solve it.

So basically, the site is working. But the thing is, that the files are not private. A user may want to ensure that the files the users upload are only visible by them, and not by someone who just guesses a bunch of urls. (eg. user1 uploads image1 which he wants to keep private, for himself =>[localhostlocalhost/upload_script/files/image1.jpg], user2 can access image1 by guessing and typing the url [localhost/upload_script/files/image1.jpg] which is what we don't want to happen. ) I have done some research and I think that this would probably require another controller for serving the files (which checks for session data). I have been "playing" with sessions etc in PHP for quite some time in the past, but I am not that familiar with them in CodeIgniter. Is this the only way? I don't think I need to create separate directories for each user, do I? Can you please tell me how to head to the right direction or give me an example?

Thanks in advance,

harris21

harris21
  • 199
  • 1
  • 5
  • 12
  • simply when you are inserting image path in the database add user id in a column user_id – Muhammad Raheel Jul 29 '12 at 11:07
  • @raheelshan it will not protect images. – Alexander Larikov Jul 29 '12 at 11:08
  • Probably you need to create a helper for `img` tag which will check if image belongs to current user. Images could be placed outside web root directory, so there will be no direct access but it might slow down your application since in that case you'll need to serve images through php script – Alexander Larikov Jul 29 '12 at 11:11
  • @AlexanderLarikov as you said, the application will slow down if all the images are placed outside the web root directory. Is it possible to create a controller in order to serve the images and check for image permissions? So if a user places a random link and finds an image just by guessing the URI, and the image was not uploaded by him he won't have access. – harris21 Jul 29 '12 at 11:19
  • @harris21 i'm afraid its not possible to restrict user's access if he guess the image URI – Alexander Larikov Jul 29 '12 at 12:37

3 Answers3

3

In order to protect files, you will need keep them outside of your web root, otherwise people will always be able to url hack their way round.

I have used the very handy mod_xsendfile for apache (if you have that kind of access to your server) which will allow you to serve files that can be protected by access control and not accessed without the appropriate credentials.

Code snippet that you could put in your CI controller to display an image (adapted from the mod_xsendfile page):

...
if ($user->isLoggedIn())
{
    header("X-Sendfile: $path_to_somefile");
    header('Content-Type: image/jpeg');
    exit;
}

If you cannot install mod_xsendfile then your only other option would be to use readfile() as TheShiftExchange says.

Pete Mitchell
  • 2,879
  • 1
  • 16
  • 22
  • And instead of $path_to_somefile I need to place something like basename($_FILES['file']['name']); ?? – harris21 Jul 29 '12 at 16:54
  • If this script is part of an upload process, then yes something like that should work. Otherwise you would want to have some sort of object/helper class that helps you locate files on the filesystem for a given user in order to determine the path – Pete Mitchell Jul 30 '12 at 11:34
2

Use PHP to return images and lock the image directory behind the webserver root. This way, before serving an image you can check the user credentials via session variable, assuring that he is allowed to view the image. Otherwise you can redirect the user straight back to the website alerting him he does not have access. Serving images like this is way slower than just serving them via webserver (apache, nginx,...) but it will enable you to have control over the downloading of the images.

To be more exact, save the image details in a database, for example having columns: id, file_path, title, uid. Everytime a user wants to download an image for example calling http://domain.com/files/download/3 you can check if image with id 3 can be downloaded for the currently logged in user. You need to write your own controller that will be doing that.

I am doing a similar thing here http://www.mediabox.si/ you can check how images are served. I am allowing thumbnail images and I am watermarking larger images visible to ordinary visitors.

Anze Jarni
  • 1,141
  • 7
  • 7
  • If user1 wants to access http://domain.com/files/image2 (by guessing the URI let's say) which is user's2 image, I want to cutout access to him by viewing this image...how will the controller check if the user has access to this image or not? – harris21 Jul 29 '12 at 11:36
  • Harris, there is no image in /files/image2. All the images must be safely stored OUTSIDE website root, so nobody can get to the directly. About the user check: you probably save the user_id in the session while authenticating login. If you have that in you session you can easily compare the user_id of the user with the required user_id from images table. – Anze Jarni Jul 29 '12 at 11:45
  • Maybe this will clear some things, check it out: http://stackoverflow.com/questions/1851849/output-an-image-in-php – Anze Jarni Jul 29 '12 at 11:54
1

The ONLY way is to store the images outside the public_html. Otherwise by definition you are opening the file to direct access.

Use a controller to check if the user is allowed to access the file and the php function readfile() to serve the file

You can read some code at one of my other questions here: Does this PHP function protect against file transversal?

And this is actually VERY fast - you won't notice a performance hit at all

Community
  • 1
  • 1
Laurence
  • 58,936
  • 21
  • 171
  • 212