Controlling Access to Images

Jim O'Halloran • September 5, 2007

php

The question was recently asked on the CodeIgniter forums, and I've run into it in various forms over the years, so I thought I'd share my answer here as well. The problem in a nutshell. I want to make some images on my site "private", and only make them accessible to certain users. How can I do this, if someone can look at the code and be able to "guess" the image URL?

Whenever I've had to solve this problem, this is what I did.... All of the images will need some sort of unique identifier, which can be something like the filename, an autoincrement number, the MD5/SHA1 hash of the file itself, doesn't matter as long as it's unique. I'll refer to this as the "image id" for the rest of the post.

You'll need to store the images outside the site's web root ensuring that no one can access them without going through your script. Create a script which takes an image id, performs the necessary permissions checks, then makes a header() call to set the appropriate MIME type and echo out the file content to the browser. In CI you'd do this in a controller method and pass the image id in one of the URL segments.

That ensures the images can't be accessed by people URL guessing id's and trying to bypass the access controls. If your image id is sequential ( i.e. increments by 1 every time a new image is uploaded) someone can easily start at 0 and just try every possible image id. If you've done what I suggested earlier, they won't get access to images they shouldn't see, but you can't stop them from harvesting all of the images they can see. To stop that from happening you want to make the image id's non-sequential. You can either use very long random numbers, or the SHA1 hash of the image contents. The advantage of SHA1 is that theres 2^256 possible SHA1 hashes, and only a very small number of those will be used within your system. The ones that are used will be distributed at random amongst all of the possible vales. So if someone started at 0 and tried all of the possible SHA1 hashes, it could take a very long time for them to find one that's actually in use. Of course it won't stop them from hammering your server trying, but it does effectively prevent them from harvesting all of the images.

So in summary, do two things:

  1. Don't allow direct image access, force it through a script and do an access check when the image is requested.
  2. Make image id's non-guessable to prevent harvesting of images.