From what I understand, images (jpeg, gif, etc.) might contain valid PHP/Python/Perl etc. code. So - anyone can create file that will be valid jpeg at the same time can be executed by PHP interpreter (here is a description).
So, I was wondering - is there a way to strip malicious code from images? would recoding images with GD or ImageMagick work?
Actual images will not contain code. But there's nothing stopping someone from trying to get an "image" file uploaded, and then try and get it to execute.
Your interpreters (Perl, PHP, whatever) should be set up so that they only execute certain file types (i.e. .php or .php5). There's no reason Perl or PHP should be parsing image files.
Just use the following common sense to protect yourself:
1) Verify the mime type of the document
2) Enforce policies that only allow uploading files of a certain extension
3) Don't accept filenames at face value. Generate your own internal file name and use a database table to keep the mapping.
4) If you're truly paranoid, find some code to check that the byte signatures are valid for the given file type upload.
You should configure your webserver not to allow your image filename extensions to get interpreted by PHP. As showed on the page linked in the question, images can contain PHP code. Always check the filename extension against a whitelist:
<?php
$whitelist = '/\.(?:jpe?g|png|gif)$/i';
if (!preg_match($whitelist, $_FILES['userfile']['name'])) {
echo "Bad filename extension.";
exit;
}
$uploaddir = 'uploads/';
$uploadfile = $uploaddir . basename($_FILES['userfile']['name']);
if (move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadfile)) {
echo "File is valid, and was successfully uploaded.";
} else {
echo "File uploading failed.";
}
?>
Related
I am currently building a PHP application that lets users upload files. I am currently at the upload page, but my verification system does not work. I am using this system to validate files as audio files.
// Set our file name and extension
$fname = $_FILES['file']['name'];
$extension = strtolower(substr($fname, strpos($fname, '.') + 1));
// Check file
if ($extension == 'mp3' && $_FILES['file']['type'] == 'audio/mpeg') {
//File is valid mp3
} else {
//File is invalid
}
Now this doesn't work, but the weird thing is when I echo out $_FILES['file']['type'] it does not echo out a mime type for audio files. When I do this for any other file type, it echoes it out successfully.
It doesn't give a mime type only for audio files. I have tried it with WAV and M4A files, and it doesn't return one with any of these either. Is it something with the file type, or do I have to edit the .htaccess file or the MIME.types file in my xampp server. Also could there be a better way to validate uploaded files?
Also note that it does this on my xampp server, and the free server that I am using for test purposes.
Thanks for the help.
$_FILES['file']['type'] comes from the browser that uploads the file so you can't rely on this value at all.
Check out finfo_file for identifying file types based on file content. The extension of the file is also unreliable as the user could upload malicious code with an mp3 extension.
I am trying to do a restricted file upload using PHP.
I have used
if (($_FILES["file"]["type"] == "application/dbase")
||($_FILES["file"]["type"] == "application/dbf")
||($_FILES["file"]["type"] == "application/x-dbase")
||($_FILES["file"]["type"] == "application/x-dbf")
||($_FILES["file"]["type"] == "zz-application/zz-winassoc-dbf"))
For me .dbf (i.e Microsoft Visual FoxPro Table type) files are not working. Please suggest to me what I should put for the content type for .dbf .
The browser uploading the file probably doesn't know it's an application/dbf mime-time, and sends it as the generic "application/octet-stream". The client/browser has to set the mime-type to be known on upload, and this can be altered by the user!
Thus MIME-type isn't reliable. If you want to be sure that it's the correct file-type/format, you'll have to examine the uploaded file.
There is another easy way for this problem , instead of inspecting the MIME type,
we can get the file extension of the uploaded file by using this function.
$filename=$_FILES["file"]["tmp_name"];
$ext = pathinfo($filename, PATHINFO_EXTENSION);
$ext = strtolower($ext);
if($ext=="png"||$ext=="gif"||$ext=="jpg"||$ext=="jpeg"||$ext=="pdf"
||$ext=="doc"||$ext=="docx"||$ext=="xls"
||$ext=="xlsx"||$ext=="xlsm"||$ext=="dbf")
{
// your code whatever you want to write;
}
Find an easy blob-upload and download of file here Blob-upload
Defining the content type is up to the browser (or other client application), making it easy to tamper with and cannot be relied upon. My guess is that your browser doesn't recognize the .dbf file and defaults to "application/octet-stream".
You can't depend on the type field of a file upload to actually determine its type. First, it can be spoofed by the client. Secondly, the client simply might not know what the file type actually is and just report 'application/octet-stream' instead.
you'll have to determine what kind of file was uploaded yourself. Fortunately, PHP provides the fileinfo extension, which can help you with determining the type of a file.
Code example based on one from php.net:
<?php
$finfo = finfo_open(FILEINFO_MIME_TYPE); // return mime type ala mimetype extension
echo finfo_file($finfo, $_FILES["file"]["tmp_name"]) . "\n";
finfo_close($finfo);
?>
http://www.php.net/manual/en/ref.fileinfo.php
Try inspecting the MIME type being passed to you when you upload a file of that type. Insert a temporary print $_FILES["file"]["type"]; somewhere in your code, then upload the file to run the code and see what it prints out! You can then copy that type and use it in your if-statement.
I need to make a website that will allow registered users to upload audio files.
I wonder is there any bullet proof practice regarding security.
The site is built in PHP
Check mime type of uploading file
mp3 -> audio/mpeg
More here: http://www.w3schools.com/media/media_mimeref.asp
You will want to check the file type carefully. This means not just doing a substring on the file name to get the extension. The extension is not a concrete indicator of what the file actually is.
As Danzan said, you will want to check the MIME type of the file, using some code like this:
if ($_FILES["audioUpload"]["type"] == "audio/mpeg") {
//proceed with upload procedure
} else {
echo "Only mp3's are allowed to be uploaded.";
}
This reduces the chances of a user uploading, say, malicious PHP code into your upload directory to basically zero.
Bullet-proof file type check is provided via combination of getimagesize, fileinfo extension and mime_content_type function (Nette Framework property):
// $file is absolute path to the uploaded file
$info = #getimagesize($file); // # - files smaller than 12 bytes causes read error
if (isset($info['mime'])) {
return $info['mime'];
} elseif (extension_loaded('fileinfo')) {
$type = preg_replace('#[\s;].*$#', '', finfo_file(finfo_open(FILEINFO_MIME), $file));
} elseif (function_exists('mime_content_type')) {
$type = mime_content_type($file);
}
return isset($type) && preg_match('#^\S+/\S+$#', $type)
? $type
: 'application/octet-stream';
You can not trust any data coming from the client, because they can be easily forged.
You can upload anything with PHP. Here's an example: http://www.tizag.com/phpT/fileupload.php
Regarding security, you have to verify that only certain people are allowed to upload stuff and that you verify the contents of what they're uploading (file size, file type, etc).
I'm implementing a user-based image uploading tool for my website. The system should allow any users to upload JPEG and PNG files only. I'm, of course, worried about security and so I'm wondering how the many smarter people than myself feel about the following checks for allowing uploads:
1) First white list the allowable file extensions in PHP to allow only PNG, png, jpg, JPG and JPEG. Retrieve the user's file's extension via a function such as:
return end(explode(".", $filename));
This should help disallow the user from uploading something malicious like .png.php. If this passes, move to step 2.
2) Run the php function getimageize() on the TMP file. Via something like:
getimagesize($_FILES['userfile']['tmp_name']);
If this does not return false, proceed.
3) Ensure a .htaccess file is placed within the uploads directory so that any files within this directory cannot parse PHP files:
php_admin_value engine Off
4) Rename the user's file to something pre-determined. I.E.
$filename = 'some_pre_determined_unique_value' . $the_file_extension;
This will also help prevent SQL injection as the filename will be the only user-determined variable in any queries used.
If I perform the above, how vulnerable for attack am I still? Before accepting a file I should hopefully have 1) only allowed jpgs and pngs, 2) Verified that PHP says it's a valid image, 3) disabled the directory the images are in from executing .php files and 4) renamed the users file to something unique.
Thanks,
Regarding file names, random names are definitely a good idea and take away a lot of headaches.
If you want to make totally sure the content is clean, consider using GD or ImageMagick to copy the incoming image 1:1 into a new, empty one.
That will slightly diminish image quality because content gets compressed twice, but it will remove any EXIF information present in the original image. Users are often not even aware how much info gets put into the Metadata section of JPG files! Camera info, position, times, software used... It's good policy for sites that host images to remove that info for the user.
Also, copying the image will probably get rid of most exploits that use faulty image data to cause overflows in the viewer software, and inject malicious code. Such manipulated images will probably simply turn out unreadable for GD.
Regarding your number 2), don't just check for FALSE. getimagesize will also return the mime type of the image. This is by far a more secure way to check proper image type than looking at the mime type the client supplies:
$info = getimagesize($_FILES['userfile']['tmp_name']);
if ($info === FALSE) {
die("Couldn't read image");
}
if (($info[2] !== IMAGETYPE_PNG) && ($info[2] !== IMAGETYPE_JPEG)) {
die("Not a JPEG or PNG");
}
All the checks seem good, number 3 in particular. If performance is not an issue, or you are doing this in the background, you could try accessing the image using GD and seeing if it is indeed an image and not just a bunch of crap that someone is trying to fill your server with.
Concerning No. 2, I read on php.net (documentation of the function getimagesize() ):
Do not use getimagesize() to check that a given file is a valid image. Use a purpose-built solution such as the Fileinfo extension instead.
I have a file (image) upload script in PHP that I use to upload and resize images... It uses a simple MIME type and size validation so only jpg images are allowed and 1MB max file size.
I recently discovered a problem. When I try tu upload a .avi file using the script, the script processes the file like its the correct MIME type and size and then just do nothing, just takes me back to the upload form without any error message. (Instead of showing a "file too big" message).
I mean, if I try to upload a .gif or .txt or something else I get an error, as expected.
If I try to upload any file bigger than 1MB I get an error, as expected.
Only when I try to upload a .avi file with more than 1MB I dont get any kind of error.....
Well, here the first par of the code:
// define a constant for the maximum upload size
define ('MAX_FILE_SIZE', 1024000);
if (array_key_exists('upload', $_POST)) {
// define constant for upload folder
define('UPLOAD_DIR', 'C:/Wamp/www/Version-1.4/posters_uploaded/');
// replace any spaces in original filename with underscores. At the same time, assign to a simpler variable
$file = str_replace(' ', '_', $_FILES['image']['name']);
// convert the maximum size to KB
$max = number_format(MAX_FILE_SIZE/1024, 1).'kb';
// create an array of permitted MIME types
$permitted = array('image/jpeg','image/pjpeg');
// begin by assuming the file is unacceptable
$sizeOK = false;
$typeOK = false;
// check that file is within the permitted size
if ($_FILES['image']['size'] > 0 && $_FILES['image']['size'] <= MAX_FILE_SIZE) {
$sizeOK = true;
}
// check that file is of a permitted MIME type
foreach ($permitted as $type) {
if ($type == $_FILES['image']['type']) {
$typeOK = true;
break;
}
}
if ($sizeOK && $typeOK) {
switch($_FILES['image']['error']) {
case 0: // ...................
I'm just modifying a build PHP code so Im no expert...
Any suggestions??
Thanks.
http://us3.php.net/manual/en/features.file-upload.common-pitfalls.php
It looks like your upload_max_filesize ini-setting is too low. This would cause no error to be displayed when you upload a very large file such as an AVI video.
The reason you're seeing the errors with text files and .jpg images is likely because the size of those files are greater than 1 MB, but below your upload_max_filesize setting in php.ini.
Try echoing the value of ini_get("max_upload_filesize") and see what the value is if you don't have access to the php.ini file directly.
As john Rasch mentioned above, any file above the php.ini max_upload_filesize will not process at all. so you'll have no chance to test the error for you. you have to assume it was not uploaded and validate it if it was.
now that I understand your scenario better I think this is what you can do:
// at the top of your script
$upload_success = FALSE;
// when successfully detected upload
$upload_success = TRUE;
// if successful upload code is never run
$display_error = "File not uploaded, may be too large a file, "
. "please upload less than 1MB"
;
print $display_error;
main point being:
You can't always detect upload files that are too big because they get cut off at a level deeper than where the scripts run.
I'd also suggest that you don't believe the mime type. Sometimes people have .png or .gif file that have been renamed to .jpg, or they could upload incorrect files intentionally. Use getimagesize to check if these are valid jpeg images.
Don't forget, when uploading files, that there are actually two directives you need to pay attention to in php.ini. One is upload_max_filesize, but the other is post_max_size. Generally, post_max_size should at least be equal to, and probably greater than, upload_max_filesize. You can't upload a file greater than post_max_size, regardless of what you set your upload_max_filesize.
An AVI file won't match the mime types you have listed in your permitted array. After doing your $sizeOK and $typeOK checks, check to see what values they hold, and how your script handles those values. That might hold the key to the behavior of your script.
Above this line:
if ($_FILES['image']['size'] > 0 && $_FILES['image']['size'] <= MAX_FILE_SIZE) {
$sizeOK = true;
}
Put this:
echo '<pre>' . printr($_FILES) . </pre>;
That will show you what is inside the FILES array, and should make this pretty simple to debug. Try uploading the AVI with the above line added to your script.
Using $_FILES['image']['type'] for checking MIME is not reliable, it's base on header of the client and can be spoofed. Take a look at fileinfo extension to check MIME base on the real contents.
Eight years later, the short answer for anyone, like me, stumbling around for the answer to checking the image MIME type with PHP before uploading:
if (exif_imagetype($file['tmp_name']) != IMAGETYPE_JPEG) {
$file['error'] = 'Your picture must be jpg.';
}
From the manual: http://php.net/manual/en/function.exif-imagetype.php