Is saving uploaded files safe? - php

I'm trying to build a simple image uploader using PHP. I do know how to code it, however I have a few concerns..
My question I had is the following: Is saving files which users send safe to save as the original file? With this I mean: Will I not get any vulnerabilities when I'm saving a file send by an user?
Let's say my PHP script does this: It retrieves the POST data, which includes a file send by a person on my website. Is it save to just move the file over to a directory, with original name, content, etcetera? Is there any harm in doing this, or should I rename these files to a random string?
If this isn't a safe way to do this, then what is? How would I verify the send content isn't harmful?

There are always vulnerabilities in storing and providing content provided by a client.
This blog post gives a good description of the vulnerabilities you could face, how they're exploited and how to protect against them. I suggest you use this as a reference: http://blog.insicdesigns.com/2009/01/secure-file-upload-in-php-web-applications/

Make sure you only process files with the correct ending. Image files are never executed by a webserver, but .php files are for example. So make sure the users don't upload any file that can be executed.
I'm not aware of file names that are harmful. For the content this is difficult to answer. I remember an attack vector with modified TIFF images on windows and one in libjpeg on *nix Systems. However you probably won't be able to find these things without completely decoding the image.

Here's another approach: use this (https://hacks.mozilla.org/2011/03/the-shortest-image-uploader-ever/) very short Image Uploader, by Paul Rouget, based on Imgur.com's API.
Instead of uploading directly to your database, let imgur do all the security and validation, then if you need to, link to or download the (safe) image via Imgur.
It's free for non-commercial and very cheap for commercial.
Basically, you can use imgur's API to safely upload images from your HTML page, with no server side code at all
Here's a live demo: (http://paulrouget.com/miniuploader/)

I would say 100% user upload file are NOT 100% SAFE
JPEG files can contain arbitrary data in them in addition to the actual image data; it's part of the spec. Thus, merely checking if an image is a valid JPEG does not mean that the file is necessarily completely harmless.
File upload without validation
HTML Form:
<form enctype="multipart/form-data" action="uploader.php" method="POST"> <input type="hidden" name="MAX_FILE_SIZE" value="100000" /> Choose a file to upload: <input name="uploadedfile" type="file" /><br /> <input type="submit" value="Upload File" /> </form>
PHP Code:
<?php
$target_path = "uploads/";
$target_path = $target_path . basename($_FILES['uploadedfile']['name']);
if (move_uploaded_file($_FILES['uploadedfile']['tmp_name'], $target_path)) {
echo "The file " . basename($_FILES['uploadedfile']['name']) . " has been uploaded";
} else {
echo "There was an error uploading the file, please try again!";
}
?>
Define a .htaccess file that will only allow access to files with
allowed extensions.
Do not place the .htaccess file in the same directory where the
uploaded files will be stored. It should be placed in the parent
directory.
A typical .htaccess which allows only gif, jpg, jpeg and png files
should include the following (adapt it for your own need). This will
also prevent double extension attacks.
deny from all
<Files ~ “^w+.(gif|jpe?g|png)$”>
order deny,allow
allow from all
</Files>
If possible, upload the files in a directory outside the server root.
Prevent overwriting of existing files (to prevent the .htaccess
overwrite attack).
Create a list of accepted mime-types (map extensions from these mime types).
Generate a random file name and add the previously generated extension.
Don’t rely on client-side validation only, since it is not enough. Ideally one should have both server-side and client-side validation implemented.

Related

PHP - why should I use pathinfo when I can get it through $_File array

why should I use this code to get the name of the file?
$filename = pathinfo($_FILES['file']['name'], PATHINFO_FILENAME)
If I could also get the name through this code:
$filename = $_File['file']['name']
Thank you very much! I'm a beginner in PHP, so sorry if the question is too dumb :D
Because $_File['file']['name'] comes from the user end, and although ordinarily it is just the file name, an ill-intentioned user can actually set it to whatever he wants (example: full path name to overwrite files in the server) and you have to filter it just like every other user input to prevent an attack vector in your system.
Same is true for everything in $_FILE, don't trust the informed MIME type, don't save files without checking if the extension is safe (saving a .php file will be a disaster) etc.
For example, I've seen a system that would trust files of type equal to image/jpeg and other image types, and then saves it without checking the actual file extension. A forged request can inject a .php shell script to this website's upload folder and be used to take control.

Website upload prevent hacking

There's an upload form on my website. I'm actually not really including or excluding file types.
Instead I'm using this:
$fileUploadName = $target_dir.md5(uniqid($target_file.rand(),true)).".".$imageFileType;
That will keep the file type but change the file name to some random cryptic like 790cd5a974bf570ff6a303c3dc5be90f.
This way a hacker cannot upload a hack.php file and the open it with www.example.com/uploaded_files/hack.php because it has changed to e.g. 790cd5a974bf570ff6a303c3dc5be90f.php. In my view it's completely safe this way. Am I right that it's safe this way?
I think only a self-executing-file could be a problem. Do self-executing-files even exist?
You should also eighter check the mime type of the file uploaded and the extension (although that can easily be faked on upload).
If you expect only images, you could also check for image width and length parameters by executing a script like this:
$size = getimagesize($target_file);
If this does not return proper values, it's no image file.
You might want to inform yourself about dangerous graphics like Gifar too.
Put uploaded files in there own folder like /etc/web/uploads/<Random> (../uploads) while the website root is at /etc/web/public/ or if you can use .htaccess create one in the uploads folder and put Deny from all
Also uniqid and rand does not generate cryptographically secure values I would also check that the uploaded file is an image anyway Link to someones isimage function http://php.net/manual/en/function.uniqid.php http://php.net/manual/en/function.rand.php
Use file_get_contents to get the users image

Serving images and saving cookies on the user's machine

I know it's possible to plant a cookie on a user's machine when he is loading an image from my server, by altering the apache settings.
However, I want to know if it's possible to include an image in HTML code that will have parameters in it and I can activate some script to log these parameters, for example:
<img src="http://www.mysite.com/myimage.jpg?somcode=123&customer=abcd" />
In return to loading the picture, I would like to save a cookie on the user's machine with somecode=123 and customer=abcd and also to save this info in my db.
Is this possible?
Thanks,
Instead of using .jpg for the filename, just use .php. Then you don't have to mess around with server configurations and normal image files still get served correctly:
<img src="http://www.mysite.com/myimage.php?somcode=123&customer=abcd" />
Then your myimage.php file:
<?php
// Your tracking and processing code here
$customer = $_GET['customer'];
// Then, either:
header("Location: /urlpath/to/the/actual/image.jpg"); /* Option 1 */
// OR:
header("Content-Type: image/jpeg");
readfile("/filepath/to/the/actual/image.jpg"); /* Option 2 */
I prefer Option 1 because it allows your web server to very efficiently serve the actual image instead of PHP (Option 2).
I'm pretty sure you could do that, Just make apache treat the .jpg extension as php files.
You could add the following to your .htaccess file in the specific folder (don't put it in the root or it will treat all JPGs this way);
AddType application/x-httpd-php .php .jpg

Select Only Images for upload through browse button

I want to make a file upload form.
But my confusion is that when i put file element in html its allow all types of files to select.
I want that browse button will only allow the images to select from the file system.
Actually it should display only images of the file system.
Thanks in advance.
Attribute "accept" with list of MIME types, does not supported by any browser.
<input type="file" accept="image/gif, image/jpeg" />
You can sort out file extension with JS, or try http://swfupload.org/
Uploadify will work for you. It allows you to specify which file types are visible for the user to choose. It can also allow the user to upload multiple files at once if you need them to. All files that are uploaded are showed in a queue with progress bars and are removed from the queue when they are finished uploading. I highly recommend it.
Generally speaking, file uploads should be validated server-side as the 'accept' attribute is not fully supported by the major browsers. Example below:
$accept = array('jpg','png','gif','bmp');
$extension = substr($_FILES['file']['name'],strrpos($_FILES['file']['name'],'.')+1);
if(!in_array($extension,$accept)){
// If the extension is not allowed show an error, else, the file type is valid
echo 'Not a valid file extension';
}

Limiting file upload type

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.

Categories