views:

1720

answers:

8

I have an upload form created in php on my website where people are able to upload a zip file. The zip file is then extracted and all file locations are added to a database. The upload form is for people to upload pictures only, obviously, with the files being inside the zip folder i cant check what files are being uploaded untill the file has been extracted. i need a peice of code which will delete all the files which aren't image formats (.png, .jpeg ect). Im really worried about people being able to upload malicious php files, big security risk! i also need to be aware of people changing the extensions of php files trying to get around this security feature.

this is the original script I used http://net.tutsplus.com/videos/screencasts/how-to-open-zip-files-with-php/

this is the class which actually extracts the zip file

<?php
function openZip($file_to_open) {
    global $target;

    $zip = new ZipArchive();
    $x = $zip->open($file_to_open);
    if($x === true) {
        $zip->extractTo($target);
        $zip->close();

        unlink($file_to_open);
    } else {
        die("There was a problem. Please try again!");
    }
}
?>

Thanks, Ben.

+6  A: 

You should probably not rely just on the filename extension, then. Try passing each file through an image library to validate that its really an image, also.

John Ellinwood
Hi, thank for the quick reply! i have never heard of this method before. How would i go about passing the files through an image library?
Ben McRae
Try WideImage for PHP. Attempt to load and verify the image based on its filename. Catch any errors that occur and you'll know if it was at least a valid image for the format specified.
John Ellinwood
@John: After all these years I had never even considered this as a solution. Thanks!
Chris Lively
+1  A: 

I don't see the risk in having renamed php files in your DB... As long as you're not evaluating them as PHP files (or at all, for that matter), they can't do too much harm, and since there's no .php extension the php engine won't touch them.

I guess you could also search the files for <?php...

Also: assume the worst about the files uploaded to your machine. Renamed the folder into which you're saving them "viruses" and treat it accordingly. Don't make it public, don't give any file launch permissions (especially the php user), etc.

Assaf Lavie
im not so worried about whats entered into the database, just the actual php files being in the extracted folder where someone might be able to run it later. Aslong as no php files can't be read, i guess thats not so bad.
Ben McRae
A: 

Now you are relying on your harddrive space for extracting. You can check fileheaders to determine what kind of files they are. there probably libraries for that.

offtopic: isnt it better to let the user select couple of images instead of uploading a zip file. Better for people that don't know what zip is (yes they exist)

PoweRoy
Hi PoweRoy, ideally that was what i wanted to implement. But its for people who have large inventories and it would take way to long for my users to upload every image individually. So this is the best possible way without me losing customers. What are these libraries you mentioned? Thanks, Ben.
Ben McRae
For multiple files, I thought this is possible in webdevelopment, i see this isn't. You can try java but if thats a desired solution...For the libraries, see John Ellinwood anser.
PoweRoy
+12  A: 

First you should forbid every file that doesn’t have a proper image file extension. And after that, you could use the getimagesize function to check whether the files are regular image files.

But furthermore you should be aware that some image formats allow comments and other meta information. This could be used for malicious code such as JavaScript that some browsers will execute under certain circumstances (see Risky MIME sniffing in Internet Explorer).

Gumbo
Image file extension? As in `"myfile.php\u0000.png"`?
Tom Hawtin - tackline
A: 

If you set php to only parse files ending with .php, then you can just rename a file from somename.php to somename.php.jpeg and you are safe.

If you really want to delete the files, there is a zip library available to php. You could use it to check the names and extensions of all the files inside the zip archive uploaded, and if it contains a php file, give the user an error message.

Marius
+28  A: 

Im really worried about people being able to upload malicious php files, big security risk!

Tip of the iceberg!

i also need to be aware of people changing the extensions of php files trying to get around this security feature.

Generally changing the extensions will stop PHP from interpreting those files as scripts. But that's not the only problem. There are more things than ‘...php’ that can damage the server-side; ‘.htaccess’ and files with the X bit set are the obvious ones, but by no means all you have to worry about. Even ignoring the server-side stuff, there's a huge client-side problem.

For example if someone can upload an ‘.html’ file, they can include a <script> tag in it that hijacks a third-party user's session, and deletes all their uploaded files or changes their password or something. This is a classic cross-site-scripting (XSS) attack.

Plus, thanks to the ‘content-sniffing’ behaviours of some browsers (primarily IE), a file that is uploaded as ‘.gif’ can actually contain malicious HTML such as this. If IE sees telltales like (but not limited to) ‘<html>’ near the start of the file it can ignore the served ‘Content-Type’ and display as HTML, resulting in XSS.

Plus, it's possible to craft a file that is both a valid image your image parser will accept, and contains embedded HTML. There are various possible outcomes depending on the exact version of the user's browser and the exact format of the image file (JPEGs in particular have a very variable set of possible header formats). There are mitigations coming in IE8, but that's no use for now, and you have to wonder why they can't simply stop doing content-sniffing, you idiots MS instead of burdening us with shonky non-standard extensions to HTTP headers that should have Just Worked in the first place.

I'm falling into a rant again. I'll stop. Tactics for serving user-supplied images securely:

1: Never store a file on your server's filesystem using a filename taken from user input. This prevents bugs as well as attacks: different filesystems have different rules about what characters are allowable where in a filename, and it's much more difficult than you might think to ‘sanitise’ filenames.

Even if you took something very restrictive like “only ASCII letters”, you still have to worry about too-long, too-short, and reserved names: try to save a file with as innocuous a name as “com.txt” on a Windows server and watch your app go down. Think you know all the weird foibles of path names of every filesystem on which your app might run? Confident?

Instead, store file details (such as name and media-type) in the database, and use the primary key as a name in your filestore (eg. “74293.dat”). You then need a way to serve them with different apparent filenames, such as a downloader script spitting the file out, a downloader script doing a web server internal redirect, or URL rewriting.

2: Be very, very careful using ZipArchive. There have been traversal vulnerabilities in extractTo of the same sort that have affected most naive path-based ZIP extractors. In addition, you lay yourself open to attack from ZIP bombs. Best to avoid any danger of bad filenames, by stepping through each file entry in the archive (eg. using zip_read/zip_entry_*) and checking its details before manually unpacking its stream to a file with known-good name and mode flags, that you generated without the archive's help. Ignore the folder paths inside the ZIP.

3: If you can load an image file and save it back out again, especially if you process it in some way in between (such as to resize/thumbnail it, or add a watermark) you can be reasonably certain that the results will be clean. Theoretically it might be possible to make an image that targeted a particular image compressor, so that when it was compressed the results would also look like HTML, but that seems like a very difficult attack to me.

4: If you can get away with serving all your images as downloads (ie. using ‘Content-Disposition: attachment’ in a downloader script), you're probably safe. But that might be too much of an inconvenience for users. This can work in tandem with (3), though, serving smaller, processed images inline and having the original higher-quality images available as a download only.

5: If you must serve unaltered images inline, you can remove the cross-site-scripting risk by serving them from a different domain. For example use ‘images.example.com’ for untrusted images and ‘www.example.com’ for the main site that holds all the logic. Make sure that cookies are limited to only the correct virtual host, and that the virtual hosts are set up so they cannot respond on anything but their proper names (see also: DNS rebinding attacks). This is what many webmail services do.

In summary, user-submitted media content is a problem.

In summary of the summary, AAAARRRRRRRGGGGHHH.

ETA re comment:

at the top you mentioned about 'files with the X bit set', what do you mean by that?

I can't speak for ZipArchive.extractTo() as I haven't tested it, but many extractors, when asked to dump files out of an archive, will recreate [some of] the Unix file mode flags associated with each file (if the archive was created on a Unix and so actually has mode flags). This can cause you permissions problems if, say, owner read permission is missing. But it can also be a security problem if your server is CGI-enabled: an X bit can allow the file to be interpreted as a script and passed to any script interpreter listed in the hashbang on the first line.

i thought .htaccess had to be in the main root directory, is this not the case?

Depends how Apache is set up, in particular the AllowOverride directive. It is common for general-purpose hosts to AllowOverride on any directory.

what would happen if someone still uploaded a file like ../var/www/wr_dir/evil.php?

I would expect the leading ‘..’ would be discarded, that's what other tools that have suffered the same vulnerability have done.

But I still wouldn't trust extractTo() against hostile input, there are too many weird little filename/directory-tree things that can go wrong — especially if you're expecting ever to run on Windows servers. zip_read() gives you much greater control over the dearchiving process, and hence the attacker much less.

bobince
Hi bobince, thanks for the great reply. at the top you mentioned about 'files with the X bit set', what do you mean by that? and also, i thought .htaccess had to be in the main root directory, is this not the case? thanks.
Ben McRae
one more question,with the vulnerability(unflatterned files)in the extractTo function of the ZipArchive class.I assume i don't have to worry about this because i use php version 5.2.8 and the fix was in version 5.2.7?what would happen if someone still uploaded a file like ../var/www/wr_dir/evil.php?
Ben McRae
+1  A: 

You might also want to consider doing mime type detection with the following library:

http://ca.php.net/manual/en/ref.fileinfo.php

Saem
A: 

Personally, I'd add something to the Apache config to make sure that it served PHP files as text from the location the files are uploaded to, so you're safe, and can allow other file types to be uploaded in the future.

Mez