I use the PHP language for file upload and want to know that the following method that I use is secure or not?
I am using simple method for file uploading,
I check the file name from $_FILES['userfile']['type']
and then if it's having an allowed file extension, I upload the file with some random number.
Like if it is abc.zip it may become 8w43x9d.zip.
Please tell me: is it a really bad method for file upload?
Randomly-generated safe filenames are definitely a good thing. However if you allowing the file extension through to the webroot you'll still need to ensure that the extension is something that won't cause any problems on the server, such as .php (OK, PHP handling should be disabled in the web server for upload directories, but still). There are also problems if you are on a Windows server, where trailing dots and spaces will confuse the filesystem; make sure you lock down the extension to a few ‘known good’ values.
Unfortunately the ['type'] property cannot be relied on at all. Some browsers won't fill a content-type in, others will put the wrong type because their OSes are set up badly (an infamously unhelpful one is that IE on Windows by default calls JPEG image/pjpeg), some will always say application/octet-stream or even text/plain.
Even the ['name'] property is unreliable; apart from browsers lying or obfuscating the value, there's always the chance a given type will have an unexpected file extension on that particular machine. Or, for Mac and Linux clients, it's entirely possible an uploaded file won't have an extension at all (or may even have the wrong extension for the type the OS sees it as).
So yeah, this is all a bit of a mess. Whilst sniffing for type from the Content-Type submission or filename extension can be useful to guess what default type a file should be, it's entirely unreliable, so it's a good thing to provide a manual method to choose the type of a file in addition. Alternatively, if you are serving the uploaded files as attachments (eg. through a PHP script setting Content-Disposition: attachment), you can often get away with just calling everything application/octet-stream and letting the user sort it out when they save it.
If you're not serving as an attachment, you may have a security problem. IE will happily sniff many filetypes you serve it for <html> tags and treat those files as HTML even if you tell it they're something else. Then it can display them inline in the browser, and lets them inject script into your security context. If you have anything significant in your security context, such as user accounts and cookies, that's a cross-site-scripting security hole. The workarounds for this are serving as attachment and/or serving from a different hostname that is not in your main site's security context (typically, a subdomain is used).
Allowing users you don't completely trust to upload files to your server turns out to actually be a much more difficult task than the trivial example code in PHP tutorials would lead you to believe. :-(
From the PHP Manual:
$_FILES['userfile']['type']
The mime type of the file, if the browser provided this information. An example would be "image/gif". This mime type is however not checked on the PHP side and therefore don't take its value for granted.
I wouldn't trust anything the user or user's browser sends me.
If you're trying to learn, I would take a look at existing secure sources, such as HTTP_Upload from PEAR. You might even consider using the Beta version.
Addition to above post, we are relying on FileInfo or SplFileInfo, so far it has proven to be the best option for our needs.
OK, in case of images we can use getimagesize($_FILES['userfile']['tmp_name']);
and can validate them but what in case of ZIP, RAR and PDF files?
Related
I have a simple form in PHP that make the conversion of a file from XML to SQL or vice versa. In this form, there is an input where the user can upload a file with xml or sql extension. I do various check (extension of the file and file size), I read the file using the value tmp_name of the global array $_FILES, I do some operation for the conversion, and then I save the modified file in my server (compressed in gz) with the original name of the file in the client machine in order to give the link to the user and download it.
My question are: There are risks of exploit with the steps that I listed above? In general, there are risks of exploit with uploading a file? Or risks, to upload the file with the original name in the client machine?
Thanks.
There are always risks of exploit when allowing users to upload files, so it's good to be worried.
You say "I do some operation for the conversion", so while I cannot comment specifically on the safety of this operation, there could be risks here depending on the operation and the content provided to it (e.g. buffer overruns, invalid data).
I'm assuming you are saving your file with a .gz extension.
Saving with the client filename could pose compatibility problems if you do not clean the filename at all. There are differences in disallowed (or problem) characters between filesystems, such as & in Unix or : on Windows. Sometimes if you simply save a file, and simply read it again your code won't "find" it, unless you escape or strip all these characters properly.
The client filename could possibly pose a risk, if the name could have for example a path embedded into it. A presented filename such as "../../../../home/user/file" could possibly trick your program into overwriting a file, as long as the permissions were very badly implemented and you are simply concatenating paths. At worst I'd say this would be an annoyance or DoS attack - limited to overwriting gzip files and "breaking" them.
The client filename could possibly overwrite another user's files? I'm not sure what your namespacing is, but a clever attacker could try to trick another user into downloading their xml/sql file by naming it cleverly.
Also if you could guess someone else's filename, you could guess the resulting URLs and war-dial through them looking for content.
All of these risks would go away, if you used a nice GUID to name the file. Or mapped it to each user's session (e.g. file1.gz is only valid to that user's session).
I generally don't use client names, or seriously validate and clean them before re-presenting them.
The main risk of uploading files with php (and other interpreted languages, as a matter of fact) is that user can upload a .php file and if it's stored inside a web-root, then execute it.
From your question, it seems you only allow certain non .php extensions. Make sure you do this check on server-side, not just on the client. Also, if you don't need to serve the uploaded file on web afterwards, store it outside of your web-root. If you force the filename and extension after upload (not keeping the original one), then you can have even more control over what's going on in your part of the system.
Other file types can also be exploited (images for example, see https://imagetragick.com/) so it's a good idea to check specifically for the file-types you want uploadable.
Writing a secure file upload PHP Script from the bottom sounds like hell to me.
The basic rules to uploading a file in no particular order:
1) Create a new file, something random, and give the new uploaded file that name
2) Check the extension
3) Check for the exif trick
4) Store all uploaded files off the web root, and give that directory no permissions to execute files.
5) make sure that the file upload function is PHP does not execute the code while uploading the file
6) Check the file size
7) Do some malware scan
8) limit filesize
So i am thinking thats a lot :)
I havent even begun writing a script for all this, because i have 3 basic questions.
1) Is my list complete, if something are missing please state which
2) is there some sort of framework that can do all this for me? Something simple, not a big huge one that can do multible other things.
3)
Is this a guide good?
http://www.sitepoint.com/file-uploads-with-php/
I would love to post code, but since this subject is big, i feel its better to ask larger.
Thanks in advance.
The “exif trick” and other measures in that article to sniff file contents are of little use in themselves. (OK, it's worth checking uploaded images are of the expected pixel size, but that's application-specific rather than a security problem.)
The article doesn't say what the threat model is that it's trying to address with filetype sniffing, but what this is commonly trying to do is prevent cross-site scripting attacks, where the attacker includes some active content in the file. Usually this is with HTML in files, which browsers (especially IE) sniff and decide to interpret as HTML even though that's not how the file is being served. Unfortunately, checking that a file begins with a PDF header, or represents a valid GIF image does not help you here because it's possible to make “chameleon” files that can be interpreted as different filetypes simultaneously.
This attack can be blocked in modern browsers by serving the files with a specific non-HTML Content-Type and an X-Content-Type: nosniff header. However there are more obscure attacks involving getting content into Flash or Java plugins that are not affected by this header, and it's not watertight against older browsers.
The really-safe way to stop XSS attacks on uploaded files is simply to serve them from a different hostname (ideally, a different domain name and IP address, but a simple subdomain is at least mostly-effective). Then you can let an attacker XSS the user-uploads-hosting site as much as they like without it having a negative effect on your main site.
Virus scanning is unlikely to prove useful for general-purpose file upload functions. If you are expecting people to use the site to exchange Windows executables then it can be worth scanning those for traditional malware, but for the general case you're typically concerned about attacks against the website itself—server exploitation, XSS, browser exploits—and those kind of attacks are not detected by AV scanners.
Your step (1) of creating a new random filename is a much better approach than “sanitising” user-supplied filenames as the linked article tries to do. Its “safe filename” function is not directly vulnerable to directory traversal, but it does still allow oddnesses like .. (on its own), the empty string, .htaccess, and filenames that would confuse a Windows server, like trailing dots, reserved names and over-long names.
You are right that secure file upload is much trickier than it initially seems, and unfortunately most tutorial code out there (especially for PHP) is pretty disastrous.
I've got a site that accepts user-uploaded files (images, pdfs, word docs, etc.) then allows other users to download them.
I realize this presents a security risk, since malicious users could upload scripts etc. that masquerade as useful files.
My question is this-- is it enough to check the mime type of the file being uploaded using PHP (mime_content_type or finfo) and set the file to read only (non-executable), or must I also store the uploaded files in a directory that is outside the web root? I would think this would eliminate most of the risk from the uploaded file, but I'm not sure. Performing a virus scan on uploaded files is not possible in this situation.
Thanks for input.
A common practice is to upload files outside the document root, and typically using randomized filenames which are then mapped to the correct item/object/post in the database. If additional permissions are needed to access the files, make sure you check them before allowing downloads, and of course you'll have only authenticated users uploading.
Fileinfo finfo_ is useful for validating most mimetypes, at least to verify that something called ".txt" is actually a text file and not a binary blob, or that a ".jpg" really appears to be a jpeg based on its first few or last few bytes. It may require some extra work sorting out MS Office mimetypes, as if I recall correctly, they all come out as application-msword. But you can then use the file extension to figure out what it is really supposed to be (xls, ppt, doc, etc).
A PHP script then supplies the downloaded file, rather than the web server directly serving it. For that reason, you should store the mime type along with it, so that you can serve the appropriate headers.
header("Content-type: application-whatever");
header("Content-length: size-of-the-file-in-bytes");
I can recommend you use every tool at your disposal to test for the file type. But know that there are other ways a hacker can implant a dangerous file.
Your best bet is to have the files be uploaded to a different server. One that can only host files.
I would check the mime type of the file but I wouldn't rely on this. Even if the file is a full blown .gif and contains a comment in its id3 tag which is a php, it can be executed with a local file include. A safer approach would be store files in the database using a long blob datatype. However this kind of overhead is crap.
The best solution from the perspective of security, scalability and perforce would be to use a no-sql database like CouchDB.
A few things to keep in mind, don't trust $_FILES[]. $_FILES['type'] could be anything the attacker wants so there is no point in checking it from a security perspective. And $_FILES['name'] could have nasty input like ../../../. Its best to rename files to the primary key and then store information about that file in a relational database (like mysql).
I am writing a small PHP application and I am not sure whether I have a security issue. So this is what the application does:
the user can upload either image files (png, gif, jpg, jpeg, tiff and a few others) or zip files
I check for mime-type and extension and if it's not an allowed I don't allow the upload (this is not the part I am worried about).
Now once uploaded I rename the file to a unique hash and store in a folder outside root access.
The user can now access the file through a short URL. I make the file accessible by setting the right mime-type for the header and then I just use readfile().
My question is whether the exploit where a jar file is included inside the image file works here? I am serving the image as a pure image.
If it does what are ways to prevent this?
Thanks.
MIME type checks will not solve the GIFAR issue. 2009's JREs are already patched, but if you want to solve the issue you can either
Serve your images from a different domain
Run a server side code to check if an image contains a valid JAR, like mentioned here
Anything else (short of denying the file to any Java enabled browser with an old enough JRE) may fail on specific cases.
Also remember that to perform a good attack with this technique your server infrastructure would have to be somewhat open (the fact that a request comes from the same domain doesn't mean that you should give any information it asks for.)
Checking the mime-type is not sufficient because that (or any other) HTTP header field can be forged. The best way to confirm that a file is a valid image is to attempt to read it as an image programatically. If it can be parsed as an image, you can be reasonably confident that it's not malicious code.
Related: ensuring uploaded files are safe
Any kind of hidden exploit like you describe should not affect the server because of the way you handle it. You're simply storing binary information, and retrieving binary information, without processing it in anyway. Browsers attempting to display exploited content might be at risk, but standard image types are fairly safe.
If you'd like to be safer, you could run an anti-virus on each uploaded file. If you're on a *nix platform, you can use the industry-standard ClamAV.
I'd be more worried of someone trying to upload a very large image file.
You can do 2 things. Serve your images from images.domain.com. this would have to be on another physical/virtual server, or firewall'd such that no open ports on the server can be accessed from that domain.
Or you can run the image file thru a java script (not javascript) like the one here. This will tell you if there is a jar file embedded in the image.
More info on this issue here:
http://www.gnucitizen.org/blog/java-jar-attacks-and-features/
I didn't actually even hear about this attack before your question, so first off, thanks for enlightening me! Googling around, it seems that there are basically two different attack vectors here. Both include the attacker luring "regular" users to a malicious site pointing to the masqueraded JAR file, and both have to do with the fact that the JAR will be executing in the "context" of your site (i.e. the origin will be your site).
First attack has to do with the applet being able to read user cookies, which basically means it'll be able to steal the user's login information for your domain.
The second one has to do with the fact that the applet is now allowed to open connections to other sockets within your domain, which is pretty bad if one of the users behind your server's firewall visits the malicious page (enabling the attacker to effectively bypass your firewall).
So this attack does not necessarily harm your server directly, but it does harm your users - and hopefully you care about your users. The two things you can do ensure their safety have already been mentioned in most of the other answers and are summarized on this page.
i check the file for its extension, and mime type - is there anything else i can be doing to help make file uploads safer?
its for an avatar (so all the images are in one folder). i was thinking about using htaccess to forbid any php execution just incase some php file found its way in there. what do you think?
Neither file extension nor mime type can give you 100% security that you are dealing with a image file. But as long as you're not going to execute the file (e.g. by using include()), that is not a problem and you do not need to check for PHP code or anything else. The only security breach imaginable using a forged image file would be something that exploits the browser's rendering engine. This is impossible to protect effectively against from server side and is the browser vendor's responsibility.
So, as long as you make sure you use is_uploaded_file() and move_uploaded_file() when handling the upload, you should be fine, at least on the image format front. Make sure you read #bobince's post below and follow the link, it contains a bunch of great information on other security aspects when dealing with files.
You could however, to provide totally maximum security, of course copy the image into a new image container using GD's imagecopy. This would erase any ID3 and other header information contained in the file, and probably destroy any exploit attempts (GD would probably choke on such a file and return an error). This works for GIF, JPEG, and PNG only, of course, and you may run into some issues like alpha channel and colour profile problems.
Never use user-submitted filenames at all; make up new ones like «random number».jpeg. ‘Sanitising’ filenames is harder than you think, especially if the app needs to be able to run on a Windows server.
For images, use the PHP getimagesize function to determine the filetype of an image, rather than looking at the highly-unreliable filename and mimetype submissions. Disallow uploads that don't parse as images.
For files that are intended to be downloaded, use the Content-Disposition: attachment header to stop IE sniffing for HTML content and displaying it in the browser.
For files that must display inline you'll have to serve them from a different hostname to your main site, otherwise HTML content inside them can cross-site-script into your security context.
Making a file upload feature secure is hard. More discussion.