views:

238

answers:

5

It's an important security issue and I'm sure this should be possible.

A simple example:

You run a community portal. Users are registered and upload their pictures. Your application gives security rules whenever a picture is allowed to be displayed. For example users must be friends on each sides by the system, in order that you can view someone else's uploaded pictures.

Here comes the problem: it is possible that someone crawls the image directories of your server. But you want to protect your users from such attacks.

If it's possible to put the binary data of an image directly into the HTML markup, you can restrict the user access of your image dirs to the user and group your web application runs of and pass the image data to your Apache user and group directly in the HTML.

The only possible weakness then is the password of the user that your web app runs as.

Is there already a possibility?

+4  A: 

If I needed security on my images directory I wouldn't expose the directory at all. Instead my img src attributes would reference a page that would take a userid and an image id as a parameter.

The page would validate that that user did indeed have access to see that picture. If everythings good, send the binary back. Otherwise send nothing.

for example:

<img src="imgaccess.php?userid=1111&imgid=223423" />

Also, I wouldn't use guessable id's. Instead sticking to something like base 64 encoded guid's.

Chris Lively
+1, good answer, but if your code validates the user's access, should it matter if the IDs are guessable?
bmb
+1 What would you think about doing some .htaccess magic and outputting something like <code>src="userid/user-image/"</code> and, treating that as a directory, lock or unlock it on a per-user basis?
D_N
but then I have binary data in the src="" part. and I would take the userid from the current session, that's more secure.
Joern Akkermann
@bmb: It wouldn't matter so much about the file id's. However, it would for member id's.
Chris Lively
@D_N: That sounds like an interesting approach. I'm not too familiar with the linux side of things, so it's hard to comment.
Chris Lively
@Joern Akkermann: I'm not sure why you'd have binary data in the src part... However, taking the userid from session is a valid approach. I tend to stay away from any session variables myself which is why I said put the userid in the src tag.
Chris Lively
A: 

With HTML5 you could use the canvas tag and JavaScript to do this.

You could perhaps do something with either CSS or a table layout to draw a picture (probably really bad performance, resolution, portability).

Either way, there is no stopping people from taking your pics. They could take a screenshot and crop it out.

As Chris mentioned in his answer, having long picture id's so that the URL for each image is not easy to guess or brute force is important. And no directory listing on your webserver directories is also.

Sean A.O. Harney
+2  A: 

I'm not sure I understand, but here goes. Instead of serving up static images that reside in an images folder - why couldn't you, using your server side technology of choice, have the images dynamically sent down to the client? That way your server side code can get in the mix and allow or deny access programmatically?

<img src="/images/getImage.aspx?id=123353 />
Eric
+7  A: 

There are other (better) ways, described in other answers, to secure your files, but yes it is possible to embed the image in your html.

Use the <img> tag this way:

<img src="data:image/gif;base64,xxxxxxxxxxxxx...">

Where the xxxxx... part is a base64 encoding of gif image data.

bmb
so xxxxxxxxxx is for the to_blob/read/to_binary command / for the binary data of the image? and must there be an "=" in the end?
Joern Akkermann
You will have to convert your binary data to base64 encoding. The result is a string of printable characters that will usually end in 1 or 2 "=" symbols, according to the base64 spec. You will need to replace the xxx's with that string, all on one line.
bmb
yeah great it works :) ... but sadly it doesn't work in the IE...
Joern Akkermann
Joern, I have it working currently in IE8 and IE7. Maybe something else is going wrong.
bmb
Oops, maybe not IE7.
bmb
I run IE tests always with the standard version that is shipped with Windows XP - I guess still many people use it...
Joern Akkermann
A: 

You could move the pictures out of the document root into a private directory and deliver them through your application, which has access to that directory. Each time your app generates an image tag, it then also generates a short-lived security token which must be specified when accessing a particular image:

<img src="/app/getImage.xyz?image=12345&token=12a342e32b321" />

Chances are very rare that someone will brute force the right token at the right time with the right image. There are at least to possibilities to verify the token in "getImage":

  1. Track all image tags in your app and store records in a database which link the randomly generated tokens and image IDs to the requesting users. The "getImage" action then checks the supplied parameters against that database.
  2. Generate the token as a checksum (MD5, CRC, whatever) over the user ID, the image ID and maybe the current day of the year, and be sure to mix in an unguessable salt. The "getImage" action will then recompute the checksum und check it against the specified one in order to verify the user's access. This method will produce less overhead than the first one.

PHP example:

$token = md5($_SESSION['user_id'].' '.$imageID.' '.$SECRET_SALT.' '.date('z'));
Thinking this over I find that it is easier to just apply the access constraints of the referencing HTML page again to the "getImage" action as someone wrote above. Nevertheless the checksum method may reduce the performance impact since it does not access any external resources to verify the request.