I'm trying to create a large, multi-page, PHP web application for work where a client enters information and receives output (think tax software). However, I can't seem to figure out how to require that a file is uploaded by the client. By files, I mean mostly PDF, txt, JPEG, etc.
There are several file upload fields that must be uploaded for the output to contain all of the necessary information to work, and several other file upload fields that are optional.
I would like to do this in PHP if possible, since as I understand it, Javascript could be turned off and that would stop the validation.
On the PHP file that processes the form you'd want to use something like this:
if (isset ($_FILES['myfile']['tmp_name']) && !empty ($_FILES['myfile']['tmp_name'])) {
// process file upload
} else {
echo 'You must upload a file in the "myfile" field to proceed!';
}
This would be in addition to other proper form validation techniques of course.
if(!isset($_FILES["fileinputname"][]))
{
die("Upload files, you must.");
}
You should check out this article
also, for html validation, you can use this.
(Most people don't like it, it's personal preference for many people)
may work, but html validation can be bypassed. IE and Safari don't support it.
http://www.w3schools.com/html5/att_input_required.asp
First of all, you shouldn't validate your forms with JavaScript only, a server-side validation should always be done.
Regarding your question, when you upload a file with PHP, you have its information in the $_FILES array. You will need to look there to know if a file has been uploaded or if it is in the right format, etc.
Related
I know there is a big amount of questions about this but I cannot get one that involves all I want to be aware of.
What I want to do is to allow the users of my webpage to upload images with a form. And I want to do this process secure, or at least as much secure I can.
I do not know too much about security in terms of deep inside of it, but I am aware of about all the consequences that a insecure webpage can produce. And I cannot be quiet thinking that my webpage is insecure or that anyone is not going to enter into my webpage because it does not have enough visits(I am realist).
At this point, I know that all the checks about security have to be done on server side instead of client side (or in both).
I know that a file can be fooled as an image and run malicious code so I searched about methods to avoid this. This is what I could find to check before store the image on the server:
From $_FILES:
$_FILES['file']['name']: To check that the file that I have uploaded have a name. To know that the file exists.
$_FILES['file']['error']: To check if the image have an error.
$_FILES['file']['size']: To check that the size of the image is bigger than 0.
$_FILES['file']['type']: To check that the type of the file is an image but it is not recommended because PHP does not check it.
General functions:
Check magic numbers to verify the image type.
exif_imagetype(): To check the type of an image.
getimagesize(): To check if it returns a 0 which means that the file is not an image.
imagecreatefromstring(): To create a new image giving a string. If it cannot be created, then is not an image.
imagepng: To create a PNG image to remove all meta-data (using imagecreatetruecolor() and imagecopy()).
But the problem I have is that I do not know if I should use all of these methods or just avoid or add some of them (because some of them seems redundant).
And my questions are:
Should I use all of them?
Have I to add another one method to be more secure?
Could be the order in which I filter the file critic? I mean, is it better to use one filter before another and viceversa? If so, what should be the order and why?
Note: I am not searching about personal opinion. I tried to gather all info I could, but I cannot be sure if it is ok or not talking about security terms. If you can put examples of something that it is forgotten it would be great.
Thanks in advance!
To answer your questions:
You don't need to use all of those methods, and which ones you use are going to be based on personal opinion. Meaning to say, there is more than one perfectly secure way to do it so don't be suprised if you get multiple different answers.
See examples below for additional checks you might have left out
Yes, the order definitely matters.
Depending on your application, the logic for any secure upload should flow something like this:
Is the user logged in? (optional)
// make sure user is logged in
if (!$user->loggedIn()) {
// redirect
}
Does the user have permission? (optional)
// make sure user has permission
if (!$user->isAllowed()) {
// redirect
}
Was the form submitted?
// make sure form was submitted
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
Is the form input valid?
// validate CSRF token
// ...
// make sure there were no form errors
if ($_FILES['file']['error'] == UPLOAD_ERR_OK) {
// make sure the file size is good
if ($_FILES['file']['size'] <= MAX_FILE_UPLOAD) {
// make sure we have a valid image type
$type = exif_imagetype($_FILES['file']['tmp_name']);
if ($type !== false) {
// make sure we check the type against a whitelist
if (in_array(ltrim(image_type_to_extension($type), '.'), array('jpeg', 'jpg', 'png'))) {
Even after validating, never trust user input
// give the file a unique name
$hash = hash_file('sha1', $_FILES['file']['tmp_name']);
$ext = image_type_to_extension($type);
$fname = $hash . $ext;
Save the file (or optionally recreate it with a library to strip out meta-data) but NEVER in a publicly accessible directory
$upload_path = '/path/to/private/folder';
move_uploaded_file($_FILES['file']['tmp_name'], "$upload_path/$fname");
The steps above are perfectly secure and more than reasonable, of course there is always a risk that some other part of your application or server might be vulnerable.
I would do the following with an apparent image upload:
1) Use is_uploaded_file() to ensure you've not been fooled into working on something else entirely
if(!is_uploaded_file($yourfile))
return false;
2) Check the mimetype with exif_imagetype() and block anything you don't want
$allowed_images = array(IMAGETYPE_BMP, IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_PNG);
$uType = exif_imagetype($yourfile);
if(!in_array($uType, $allowed_images))
{
unlink($yourfile);
return false;
}
3) Use Imagick to remake the image and remove all comments and metadata:
$image = new Imagick($yourfile);
$image->resizeImage($image->getImageWidth(), $image->getImageHeight(), Imagick::FILTER_CATROM, 1);
$image->stripImage(); // remove all comments and similar metadata
4) Write the replacement image to the filesystem and erase the original file:
$image->writeImage("/path/to/new/image");
unlink($yourfile);
5) Upload this image to S3.
// your S3 code here
6) Make a note of the image's S3 URL in the database or wherever.
// your database code here
7) Erase the replacement image.
unlink("/path/to/new/image");
If you get enough responses, you might have a good answer! :-)
Operating System
Make sure you have a dedicated volume for the files. Or, at minimum, have quota set on the directory. Make sure you have enough inodes and such, if on Linux/Unix. A bunch of small files can be just as deadly as a few gigantic files. Have a dedicated uploads directory. Set where the temp files should go in your php.ini. Make sure your file permission are safe (chmod), too. Use Linux ACLs, if necessary, to fine tune permissions. Test, test, test.
PHP
Incorporate the knowledge found here into your uploaded file handling algorithm PHP Manual: POST method uploads. Take the MAX_FILE_SIZE bit with a grain of salt.
Make sure you know what your max up load file size is. Set it accordingly. There may be other file related settings. Be sure to lock those in before getting around to the $_FILES superglobal.
Do not work with the uploaded files directly, and do not use the name attribute at all to give the file a real file name. Use, is_uploaded_file() and move_uploaded_file() appropriately.
Use tmp_name appropriately.
Be wary of null bytes on file names! Yes, you still need to filter and validate any string that represents user input (especially if you intend on using it any way).
First things first, check for the presence of a file.
Second, check the size in bytes.
If anything in #5 or #6 fail, the validation process should end. For a robust routine, incorporate the idea that at sometime you may want to upload multiple files at one time (PHP Manual: Uploading Multiple Files). In that case, the $_FILES superglobal may not look like you would expect. See the link above for more details.
GD
You've got the general idea about using these functions to open the submitted file (without using the user submitted name, that is). Just come up with a logical series of progressive steps. I don't have those steps, but if meta-data can be a problem, that would seem high on the list of GD stuff to try early (after basic file presence and size stuff). I could be wrong though.
My requirement is to upload multiple jpeg file in one go, the problem is when some one uploads any other extension file along with jpeg, in this case the alert comes after file reaches to server i.e., have 15mb mp3 file, if we upload it, error comes after 15mb data transfer to server...
Is there anything I could do to check file extension before actual data transfer.
I m using cake php.
Appreciating your help and support.
You would only be able to achieve this with client side scripting if you want to trap the file format BEFORE the submission of data if this is a standard HTML form that has been generated with Cake. This is because the HTML form and browser are handling the data posting.
It is true that you could invoke some sophisticated PHP WITH scripting to do extension checking but it is probably not necessary here, you need scripting regardless to react to your "submit" button being pressed by the user
Have a look at the jQuery Validation plugin, and notably its "Accept" attribute here for example or use a simple custom built JS extension checker.
$("#myform").validate({
rules: {
myfield: {
required: true,
accept: "jpg"
}
}
});
I have a PHP website where people can fill out help-tickets. It allows them to upload screenshots for their ticket. I allow gif, psd, bmp, jpg, png, tif to be uploaded. Upon receiving the upload, the PHP script ignores the file extension. It identifies the filetype using only the MIME information, which for these filetypes is always stored within the first 12 bytes of the file.
Someone uploaded several GIFs, which when viewed with a browser, the browser said it was invalid, and my virus scanner alerted me that it was a injection (or something like that). See below for a zip file containing these GIFs.
I don't think only checking header info is adequate. I have heard that an image can be completely valid, but also contain exploit code.
So I have two basic questions:
Does anyone know how they did injected bad stuff into a GIF (while still keeping a valid GIF MIME type)? If I know this, maybe I can check for it at upload time.
How can I prevent someone from uploading files like this?
I am on shared hosting so I can't install a server-side virus
scanner.
Submitting the info to a online virus scanning website
might be too slow.
Is there any way to check myself using a PHP class that checks for these things?
Will resize the image using GD fail if it's not valid? Or would the exploit still slip through and be in the resized image? If it fails, that would be ideal because then I could use resizing as a technique to see if they are valid.
Update: Everyone, thanks for replying so far. I am attempting to look on the server for the GIFs that were uploaded. I will update this post if I find them.
Update 2: I located the GIFs for anyone interested. I put them in a zip file encrypted with password "123". It is located here (be careful there are multiple "Download" buttons on this hosting site -- some of them are for ads) http://www.filedropper.com/badgifs. The one called 5060.gif is flagged by my antivirus as a trojan (TR/Graftor.Q.2). I should note that these files were upload prior to me implementing the MIME check of the first 12 bytes. So now, I am safe for these particular ones. But I'd still like to know how to detect an exploit hiding behind a correct MIME type.
Important clarification: I'm only concerned about the risk to the PC who downloads these files to look at them. The files are not a risk to my server. They won't be executed. They are stored using a clean name (a hex hash output) with extension of ".enc" and I save them to disk in an encrypted state using an fwrite filter:
// Generate random key to encrypt this file.
$AsciiKey = '';
for($i = 0; $i < 20; $i++)
$AsciiKey .= chr(mt_rand(1, 255));
// The proper key size for the encryption mode we're using is 256-bits (32-bytes).
// That's what "mcrypt_get_key_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC)" says.
// So we'll hash our key using SHA-256 and pass TRUE to the 2nd parameter, so we
// get raw binary output. That will be the perfect length for the key.
$BinKey = hash('SHA256', '~~'.TIME_NOW.'~~'.$AsciiKey.'~~', true);
// Create Initialization Vector with block size of 128 bits (AES compliant) and CBC mode
$InitVec = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC), MCRYPT_RAND);
$Args = array('iv' => $InitVec, 'key' => $BinKey, 'mode' => 'cbc');
// Save encoded file in uploads_tmp directory.
$hDest = fopen(UPLOADS_DIR_TMP.'/'.$Hash.'.enc', 'w');
stream_filter_append($hDest, 'mcrypt.rijndael-128', STREAM_FILTER_WRITE, $Args);
fwrite($hDest, $Data);
fclose($hDest);
As for the first question, you'll never really know if you're not able to retrieve any logs or the images in question, because there are many things these exploit may have targeted and depending on what's the target the way the exploit was put into the file can be completely different.
Edit: W32/Graftor is a generic name for programs that appear to have trojan-like characteristics.
After opening the file 5060.gif in a hex editor, I noticed the program is actually a renamed windows program. Although it's not a browser exploit and thus harmless unless it's actually opened and executed, you'll have to make sure it isn't served with the MIME type defined by the uploader because a user may still be tricked into opening the program; see the answer to the second question.
As for the second question: to prevent any exploit code from being run or a user, you'll have to make sure all files are stored with a safe extension in the filename so they are served with the correct MIME type. For example, you can use this regular expression to check the file name:
if(!preg_match ( '/\\.(gif|p(sd|ng)|tiff?|jpg)$/' , $fileName)){
header("415 Unsupported Media Type");
die("File type not allowed.");
}
Also make sure you're serving the files with the correct Content Type; make sure you don't use the content type specified with the uploaded file when serving the file to the user. If you rely on the Content-Type specified by the uploader, the file may be served as text/html or anything similar and will be parsed by the users' browser as such.
Please note that this only protects against malicious files exploiting vulnerabilities in the users' browser, the image parser excluded.
If you're trying to prevent exploits against the server you'll have to make sure that you won't let the PHP parser execute the contents of the image and that the image library you are using to process the image does not have any known vulnerabilities.
Also note that this code does not defend you against images that contain an exploit for the image parser used by the users browser; to defend against this, you can check if getimagesize() evaluates to true as suggested by Jeroen.
Note that using getimagesize() alone isn't sufficient if you don't check file names and make sure files are served with the correct Content-Type header, because completely valid images can have HTML / PHP code embedded inside comments.
You can use the getimagesize() function for this. If the image is invalid it will simply return false.
if (getimagesize($filename)) {
// valid image
} else {
// not a valid image
}
It's worth noting that this isn't 100% safe either, but it's the best you can do as far as I know.
Read more about this here.
I dont know much about image formats, but recreating the images and then storing the result, I feel has a good chance of eliminating unnecessary tricky stuff. Especially if you strip all the meta data like comments and all the other types of optional embedded fields that some image formats support.
You can try phpMussel on any php script that accepts uploads. The file will be scanned using ClamAV signatures, plus some internal heuristic signatures that look for this type of intrusion specifically.
1) You're never going to know exactly what the problem was if you deleted the .gif and your A/V didn't write a log.
Q: Is the .gif in question still on the server?
Q: Have you checked your A/V logs?
2) There are many different possible exploits, which may or may not have anything directly to do with the .gif file format. Here is one example:
http://www.phpclasses.org/blog/post/67-PHP-security-exploit-with-GIF-images.html
3) To mitigate the risk in this example, you should:
a) Only upload files (any files) to a secure directory on the server
b) Only serve files with specific suffixes (.gif, .png, etc)
c) Be extremely paranoid about anything that's uploaded to your site (especially if you then allow other people to download it from your site!)
On very usefull tip to prevent problems with injected PHP came from my host's system admin: I have a site where people can uploaded their own content. I wanted to make sure the directory where uploaded images are served from doesn't run any PHP. That way someone could even post a picture named "test.php" and it would still NEVER be parsed by PHP if it was in the upload directory. The solution was simple: In the folder the uploaded content is served from put the following .htacess:
RewriteEngine On
RewriteRule \.$ - [NC]
php_flag engine off
This will switch off the PHP engine for the folder, thus stopping any attempt to launch any PHP to exploit server side vulnerabilities.
Late response, but may be useful for somebody.
You may try such approach:
//saves filtered $image to specified $path
function save($image,$path,$mime) {
switch($mime) {
case "image/jpeg" : return imagejpeg(imagecreatefromjpeg($image),$path);
case "image/gif" : return imagegif(imagecreatefromgif($image),$path);
case "image/png" : return imagepng(imagecreatefrompng($image),$path);
}
return false;
};
Simple question. Is there a way to only allow txt files upon uploading? I've looked around and all I find is text/php, which allows PHP.
$uploaded_type=="text/php
When you upload a file with PHP its stored in the $_FILES array. Within this there is a key called "type" which has the mime type of the file EG $_FILES['file']['type']
So to check it is a txt file you do
if($_FILES['file']['type'] == 'text/plain'){
//Do stuff with it.
}
It's explained very well here. Also, don't rely on file extentions it's very unreliable.
Simply put: there's no way. Browsers don't consistently support type limiters on file upload fields (AFAIK that was planned or even is integrated into the HTML standard, but barely implemented at best). Both the file extension and mime-type information are user supplied and hence can't be trusted.
You can really only try to parse the file and see if it validates to whatever format you expect, that's the only reliable way. What you need to be careful with are buffer overflows and the like caused by maliciously malformed files. If all you want are text files, that's probably not such a big deal though.
You could check the mime type of the uploading file. In codeIgniter, this code is used in the upload library:
$this->file_type = preg_replace("/^(.+?);.*$/", "\\1", $_FILES[$field]['type']);
The variable $this->file_type then used to check the upload configuration, to see if the uploaded file is in allowed type or not. You can see the complete code in the CodeIgniter upload library file.
You need to check the file extension of the uploaded file.
There is Pear HttpUpload, it supports this.
I'm creating a script to only upload zip files and I have the following validation allready:
if($itemtype != "application/x-zip-compressed"
&& $itemtype != "application/zip" && $itemtype != "application/octet-stream") {
throw new exception("Your file should be a zip file!");
}
but I have a file with file type application/force-download but when I look in the mime types I can't find it, should I still add it in the validation (see code above)? the file I try to upload should be a normal zip file ... well that's what I thought
Fix your code in your example and make your question more clear.
That said, it's unclear whether you're trying to validate an uploaded file or a downloaded file.
I'm going to take a wild guess and say that you might be trying to serve a file that's already uploaded. Mimetypes are a pretty bad way of validating that, but your problem might actually lie with your webhost. In similar situations in the past, I've seen shared hosting providers inserting all kinds of headers without my permission, resulting in problems similar to what you might be experiencing, were your question more precise. Unfortunately, the solution in that particular case was to re-write the entire serving process for the download in php, which had a whole bunch of problems of its own.
You need to check file extension, not the content type. Especially, because different browsers can send different content types.