This question already has answers here:
Security threats with uploads
(4 answers)
Closed 9 years ago.
So I have a client wants a photography site where users can upload their photos in response to photography competitions. Though technically this isn't a problem, I want to know the risks associated with allowing any user to be able to upload any image onto my server. I've got the feeling the risks are high...
I was thinking of using something like this http://www.w3schools.com/php/php_file_upload.asp
If I do let anonymous users upload files, how can I secure the directory the images (and potentially damaging files) will be uploaded into?
if you want to be sure that the image is a real image you can load using gd http://www.php.net/gd
if the gd resource is created correctly then the image is a real image
first detect the mime using:
getimagesize($filename);
then, for example if it is a jpeg load into gd:
$gdresource = imagecreatefromjpeg($filename);
if $gdresource is valid/created without warnings, the image is valid and not corrupted... getimagesize() is (probably) not good enough to detect corrupted images
also, another important note... don't rely on $_FILES['blabla']['name'] because it could contain non valid utf-8 sequences (assuming that you are using utf-8 for example) and it could be a potential attack mechanism, as any user input
so you'll need to validate / sanitize that as well
$originalFileName = $_FILES['blabla']['name'];
$safeOriginalFileName = iconv('UTF-8', 'UTF-8//IGNORE', $originalFileName);
// more additional checks here. for example filename is empty ""
move_uploaded_file(...., $safeOriginalFileName);
also, remember that $_FILES['blabla']['name'] contains the file extension, which may not be correct. so you'll need to strip it out and use the actual correct extension (that you previously resolved using getimagesize() + imagecreatefrom*())
$safeOriginalFileName = basename( $safeOriginalFileName ); // removes the extension
$safeOriginalFileName = $safeOriginalFileName . ".jpg"; // correct extension
hope this helps :)
also as DaveRandom pointed out, don't rely also on $_FILES['blabla']['type'], use instead as I suggested getimagesize() + imagecreatefrom*()
The uploaded file is stored at a temporary location, this location can be found in the $_FILES variable.
When your script accepts the uploaded file, you can use move_uploaded_file() to move it to the location of your choice.
So even the user is anonymous you are in control what to do with uploads and whether to accept them (eg based on content, size, etc.) or not.
Furthermore, the (anonymous) user provides the file and accompanied details. So, if you blindly use these details, your are vulnerable (a user with bad intents is probably providing the wrong details, to make it legit). So, if you need these details, gather them yourself (instead of using $_FILES)!
For more information see the PHP documentation
You will have to research a bit, but mainly these are the main hints:
The basic security you can have is to check actually the image's MIME type and extension. Although this is certainly easy to forge.
Use binary safe functions like readfile(), fopen() and file_get_contents(), I don't remember exactly which ones but there was a few php functions that had security issues handling files, research which ones are and avoid them.
There are some functions out there using preg_match() and similar that will check if there's something similar to a script in the file you are reading. Use them to make sure there isn't hidden scripts. This will slowdown the process a bit as preg_match() can be resource expensive reading big files but it shouldn't be very noticeable
You could also trigger an antivirus to run on the files uploaded as the email services do.
As far as I know the potentially damaging images would normally contain scripting languages, like php code or javascript to try XSS attacks, there are a lot of dangers out there, so I guess you can't guarantee 100% de safety of the files, but keep having a look periodically to see all the new dangers and ways to avoid them.
Related
I am wondering what the difference between $_FILES["file"]["type"] and end(explode(".", $name), as well as an appropriate method to determine if the retrieved file type is really the correct content of the file.
For example, what's the best way to sort a file that was named "image.exe" and renamed "image.jpg."
I've seen a lot of talk about MIME types, but it seems that method has been deprecated.
This is the correct way to read an extension:
$ext = pathinfo($name, PATHINFO_EXTENSION);
The correct way to check if something is an image is to try and read it with an image tool, such as imagemagick or GD. GD is easier, but imagemagick is better at handling big images, such as one uploaded from a 12 megapixel camera.
If you're worried a jpg is really an exe, the only way to safely process it is to read it as a jpg and try to create a new jpg (typically resizing it at the same time). Gmail does this with image attachments.
Also beware a real jpeg might have some kind of exploit, so even if it is an image it is not safe to just pass it onto the user anyway. You really should resize it to create a new "safe" jpeg and then give that to the user. You could make a new jpg the same size if you want, but passing the original data on to other users is dangerous.
I wouldn't even allow an admin user for your website to access a jpg uploaded by a random internet user. It could be used to hack into the admin's PC.
$_FILES["file"]["type"] is supplied by the user's browser and hence useless for security.
The file extension, as you note, is easy to fake as well.
If you want to make sure an image is an image, the easiest way is to run getimagesize() on it.
If you want to make super sure and remove any and all metadata possibly embedded in the image, use GD's imagecreatefromstring() to copy the image to an empty canvas (but be prepared for a possible slight loss in quality.)
For other file types, there apparently is the Fileinfo library now. It uses the underlying Operating System's mime.magic file to estimate a file's type by checking certain characteristics and "header bytes" in the file.
If you're just working with image files, then getimagesize() should do it:
http://www.php.net/manual/en/function.getimagesize.php
It will return the image type as an element of the array it returns or FALSE if the file is not a valid image.
Beware of double extensions when using end(explode(".", $name).
Apache will read the right-most extensions if 2 extensions are given which map onto the same meta information. E.g. file.gif.html will be associated as an html file.
As stated above, the client-provided browser info is useless as a security measure.
Best bet - getimagesize();
As with everything, there are ways around it. A malicious code comment could be added to the picture that bypasses the getimagesize() check because the header is still valid
I was wondering if you know about any good and accurate PHP library or file I can include in my script in order to analyse the content of X file and then check if it is an especific type like .doc, .docx .jpg, etc.
I know PHP offers a big number of libraries that we could use to check them, but they're not that accurate at all, some just checks the file extension or the file header (they don't even know if the file is broken or not)
What I request is for something very accurate, simple and faster (probably I'm requesting too much) but any link or suggestion will be accepted and appreciated, Thank you!
As far as I know, no such library exists; it also wouldn't make sense to have one.
let's say I have jpeg image I would like to analyse, the headers probably would be okay but the image itself is broken, and when I want to convert them or cut them for thumbnails (with the GD library which is the one I use) the functions (mostly imagecreatefromjpeg) will throw me errors, and in order to create a good thumbnail I need a valid image.
The best place to catch a malformed JPG file with malformed headers is when GD errors out while trying to process it. Just deal with that in a transparent and useful way (= let the user know that something went wrong). Why add extra code that would essentially have to do the same thing?
By handling the error when it occurs, you can also catch issues that a simple analysis of the file wouldn't reveal anyway - for example, GD can't deal with CMYK JPGs. Still, CMYK JPGs are perfectly valid files. Another example is files that are too big to be processed on your server.
Of course, you can do header or size checks beforehand on every uploaded file. But a separate check that goes as deeply as you want it doesn't make sense.
Apart I would like to have it to prevent virus or code injection..
This isn't a realistic goal. What if the library you open the file with to check it is vulnerable to the injection?
Also, injections like this are very rare; library vulnerabilities tend to be widely publicized, and patches quickly provided. Just keep your machine up to date.
If you really need enterprise-grade virus protection, get a server-side virus detection product.
What i did for this was to open the file, read it, and search for the file headers. most of them are available in their wikipedia format definition.
%PDF for pdf, first 4 chars.
%PNG for png, first 4 chars.
Havent seen yet a library to do that.
After reading a lot of articles. I would say, so what should I actually do to secure my site from hack attempts via the file upload?
From these links:
This link says that MIME IS USELESS and that EXTENSION IS THE WAY TO GO. But in the end the 2 parties are just arguing and if I'm correct BOTH agreed to say that both MIME or EXTENSION has a security hole. A lot of hate over there.
This link agrees to say that MIME is also useless AND EXTENSION is also just not FOOL PROOF as HTML or JAVASCRIPT code can be inserted in a GIF image file (or others) and can be misinterpreted by IE leading to a quick backdoor entrance for malicious code(I really wish everyone would just vote to stop the use of IE. Its like it was made to use as a hacking browser.)
This link says to give the file a NON-EXECUTABLE PERMISSION so that no-matter what it is it wont run (but would this protect us from xss/html/javascript/etc. embedded in the images like the one mentioned in the 2nd statement? If giving the file a non-executable permission would protect us from those embedded threats. Would it also protect us from other threats? Are there other forms of hack that can bypass this approach?)
And then there's this link that says "Re-process the image" other methods are just "fun boring for hackers.". Which is kind of in a way a solid way of identifying if the IMAGE is an IMAGE(IMO, cause imagick wont convert a non image right? Not sure. Haven't dive into it yet. Looked deep).
So what is the best and secure way to protect our sites from file upload threats?
If we check for all:
VALID MIME TYPE
VALID EXTENSION
GETIMAGESIZE() CHECK
ENSURE NON-EXECUTABLE PERMISSIONS
REPROCESS THE IMAGE
Would that be enough? For a SAFE SECURE Image File Upload?
mime-type is easy to fake, file extension is easier to fake. Use them if you need a clue on what the file type is, assuming the user is a good guy. Don't rely on it.
My point exactly
Give the file non executable permissions is a good idea. It is useless from a web security point of view. Are your .php files executables? No. Are they still processed by the web server? Yes.
This is the way to go. Open the file with imagick for example. If imagick complains about the file format, then don't keep it.
I want check uploading file type, tell please where way is reliable for this, in where case obtain I more exactly info about file type? in this:
$_files['uploaded_file']['type']
or in this:
$imgType = getimagesize($_files['uploaded_file']['tmp_name']);
$imgType['mime'];
?
$_FILES['uploaded_file']['type'] is user input - it is an arbitrary string defined by the client. It is therefore not safe to use for anything at all, ever.
getimagesize() is a much safer way to do it, but it does not protect you completely.
You also need to:
Store the file on your local server with a name completely of your own devising. It is not safe to rely on any user input for generating local file system file names.
Use GD to copy the pixel data from the source file to the destination file. getimagesize() only looks at the meta data associated with the file and does not look at the file data. It is possible to hide malicious code inside something that looks like an image. Once you have resampled the image, ensure the uploaded file is deleted from the server.
Ensure that the file is stored in the local file system with the minimum required permissions and a restrictive owner/group.
$_FILES[...]['type'] is never reliable, it's an arbitrary user-supplied value. getimagesize, exif_imagetype or finfo are the preferred ways to check what you've got. Also see Security threats with uploads.
I Trust getimagesize
Because any one can edit the uploaded file type Using any HTTP/HTTPS headers like tamper data addons in firefox
In practice you should probably use the meta data that comes with the image, ($_files['uploaded_file']['type']), however this could be tampered with before upload.
If you're just after the size, use this as it will be faster than actually measuring the image with getimagesize. However if you are after filetype information, it may be best to check beyond what the file 'says' it is as it's all to easy to sneak an executable through a naive check.
Using getimagesize() will provide you with more consistent mime information since it uses it's environment mime-type definitions for files (GD module mime database).
If you solely rely on $_FILES['uploaded_file']['type'], then it will contain mime-type as defined on client computer (i.e. browser) or browser may not even send mime-type.
One pretty stupid example of this is checking if($_FILES['uploaded_file']['type'] == 'image/jpeg'), which may fail when using IE6/7 that will send 'image/pjpeg' mime-type
An alternative to both of these is using mime_content_type() but it is being deprecated as PECL module so as of PHP 5.3.0 there are FileInfo functions that should work much more consistent with any file type - though I haven't tested it.
For more overview of mime-types check this SO article: How do I find the mime-type of a file with php?
Don't ever trust $_FILES["image"]["type"]. It takes whatever is sent from the browser, so don't trust this for the image type. so use getimagesize() or if you want to be on more safe side use finfo_open
Source :http://php.net/manual/en/reserved.variables.files.php
After reading http://dsecrg.com/files/pub/pdf/XSS_in_images_evasion_bypass_(eng).pdf, it is clear that allowing image uploads from users opens you to XSS attacks.
I wasn't able to find any PHP examples of how to screen an uploaded image for XSS attacks.
I found one for CodeIgniter, which I am using. The function is xss_clean($file, IS_IMAGE), but there is only 1 sentence of documentation for it, so I have no idea how it works and a comment in their forum said it had an unreasonably high rate of false positives, so it's not usable in production.
What do you recommend to prevent XSS attacks within an uploaded image?
As long as you keep the extension correct (and your users are diligent about updating their browser) image injection should not be possible.
For instance, if someone uploads alert('xss'); as an image and you have <img src='that-image.png'>, it will be emitted as a png and the JavaScript won't execute (at least back to IE7). What's important is that you rename the images appropriately.
If you have php > 5.3 and the finfo PECL extension, you can use it to get the mime type of the file and have a whitelist of types you will allow (png, jpg, gif I would imagine). If you are on a Linux machine, file may help you with that as well.
In CodeIgniter there's many way to prevent the XSS. You can enable it when getting the value like ->post('data', true). The second parameter is the XSS bool.
Also, don't use the HTML IMG tag. Use the CodeIgniter one that will clean, look and make it easier to display the image.
Just my two cents!