Jump to content
MakeWebGames

Security and file uploads


a_bertrand

Recommended Posts

I saw somebody worried about a security issue while working with a file upload. For those of you which are a bit... lost with such kind of issues, I thought it may help to explain a bit the issue:

As soon as you allow file uploads you may end up with security holes if you don't manage the upload correctly and simply store the uploaded file on some browsable directory.

Let's take an example:

I have a simple "upload file" (whatever it is) page, which once the file is uploaded stores it in the images sub directory.

Now some "hacker" uploads some script instead of an image and call the script directly like "http://mysite.com/images/myuploaded_script.php"

Of course this could be an issue as it allows everyone to run code on your server.

To avoid such situation you may:

1) Check that kind of file is uploaded. For example, try to open the file with imagecreatefrompng or whatever function, and check the width / height of it.

2) Make the image upload directory not browsable (via .htaccess) and go through a PHP to view it like:

<?php
header("Content-type: image/png");
readfile("images/thefile.png");

This will ensure you only give back the data as stored on the server, however then this script could potentially serve any file (not in this case as I hard coded the path), so you will have to check what to display here and not allow to browse outside of the given path.

3) Instead of storing on the file system, stores it in a database, the advantage is that if you run the web server on a cluster or make just a backup of the DB you will as well backup such document. The drawback is the speed of the database which will quiet certainly be slower than a simple file access. You will need of course again some script to serve back the image.

Keep in mind you should use readfile, or file_get_contents NOT INCLUDE as it will then execute the code!

There is other kind of more creative ways (both for hacking and for protecting), but I think I gave some hints.

BTW Supdinski this is not against your script, I didn't checked it, so I can't judge it nor it was my intention to do so.

If you guys want to share more info, or have questions, please do so.

Link to comment
Share on other sites

Extra steps you can go through:

In the upload script, "$_FILES" gives you the mime of the uploaded file. Make Sure the mime is equal to "image/<subtype>".

If you are getting the image from another website, get headers will work, you may have to doo a little parsing (not much) to get the "Content-Type"

------

Directly after the upload. Check to make sure exif_imagetype(path_to_file) and getimagesize(path_to_file) does NOT return false.

------

Disable php from being ran in the directory which you are storing images. This is done in an htaccess file stored in that directoy (or in a parent directory with directory association (example: <directoy path_to_directory>)).

Settings for .htaccess should be something like the following

Options -All

RemoveType .php .php3 .php4 .php5 .phtml .phps

Link to comment
Share on other sites

Content types can be a nuisance on some hosts, especially shared ones.

jQuery has been reported(and I've found) that requests will fail when a content-type is miss-validated by the server.

But most of all, it's not very effective to serve files through a PHP script, for multiple security reasons.

My suggestion is to know what you want to serve, and setup a isolated environment to do it.

Much like a chroot jail for processes, one can do the same for a specific directory on the server.

This is normally done by setting the permissions on a specific folder on the filesystem.

Another approach I personally use, is to assume the file doesn't have a format.

After that, if I want to work with images, I look for tell-tell signs of compression, something most image file formats have.

Then, I grab the first 8 bits of a file, and make a guess as to what file it is.

If it doesn't match a specific set of rules, I discard the file or replace the contents with a stream of /dev/zero to maintain validity on the system.

For those of you that feel lost with regards to reading 8bits:

Most file formats can be identified by looking at the first few characters within the file.

Open up a .JPG file, and you should see "JFIF".

Open up a .PNG file, and you should see "PNG".

Open up a .BMP file, and you should see "BMP".

It's also a very handy method for determining if a file is corrupted.

There are other techniques to use, such as ImageMagick integrations, but let's not go into depth about them just yet.

Link to comment
Share on other sites

The mime type of the $_FILES array is not really useful. It's easy to send over a PHP file with a mime type of a JPEG.

It's an extra precaution. And there's nothing wrong with taking extra precautions just to be safe. Besides... Not everyone is a hacker. A legit user may try to upload a file type that you do not want uploaded. And right there if the content-type is invalid you can catch it right there and tell them that it's invalid, without having to go any further or use a more memory intensive function.

The first 8 bits of a file can be faked just as easily as the content-type, especially in a php file.

I have just created a php file where php thought the mime type was image/png, this file also passed the getimagesize test, and passed the exif_imagetype. Also a lot more than the first 8 bits, and a lot more than the last 8 bits of the file were valid png bits, so it would have passed the 8 bit test also. But I would have been saved because I would have had the execution of php scripts disabled in the particular folder.

Really... Code which can contain any ascii character coming in any order can be faked (like a compressed image, which like spud said most are compressed) So the only way that I can think of to be sure is to actually check every bit of the file.

But really the extra precautions are for different lvl hackers.

There's the lvl1 hacker that changes file extension and tries to upload.

//file fails all tests.

The level 2 hacker who tries to change the content-type.

// passes some tests

Then theres the lvl 3 hacker who will go into the code.

//can pass all tests depending on tests given.

So basically the more safe you want to be, the more memory intensive the tests will be.

Doing a lvl 3 hacker test to catch a lvl 1 hacker is like calling S.W.A.T. to catch someone who stole a salt and pepper shaker from a restaurant.

Time and memory could have been saved.

Link to comment
Share on other sites

Checking if a whole file is valid seems overkill. The reason why I say that is the following:

1) If you don't include the file, don't execute it by any other mean, and don't allow remote execution of it (like accessing the file from the browser) you may even allow PHP upload. I mean if it will not be executed it will not harm.

2) Checking the validity if the file pass the getimagesize, the header is correct etc, well checking the whole image will not help you much. How can you detect a valid image or not? Will you make some image analysis? Sorry but doesn't make much sense, would be simply too much time consuming and bring little.

For the mime type, yes you are right it's a first precaution, which could already filter out some.

BTW It should be the first 8 bytes not 8 bits... as you need 8 bit to form 1 byte ;)

Link to comment
Share on other sites

Lol sorry when typing I tend to use both bit and byte as if the were the same, it's a bad habit because I only do it to type one less letter, hoping that most people will know what i'm talking about.

I didn't mean that you should create something to validate the whole file. With the precautions I put in my first post, it wouldn't even be necessary. I was just pointing out that almost anything can be faked, and that if you really wanted to positive of what was being uploaded, then you would have to check the whole file byte by byte. Yes it would be time consuming to make and memory consuming to execute. I wouldn't even recommend it. But, I'm just saying if you really wanted full control over what is and what is not being uploaded then you would want to do this.

And yes, getimagesize does check the headers. But that's not all it does. It has to find the meta data then in the meta data it has to find the image size.

Basically this precaution is to check if a part of the image file structure is there and is valid. But, why would you do all of that when you already know that the mime type is invalid? More time and memory wasted on a possible hacker.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...