Rename uploaded files to avoid overwriting - php

I have this simple script which works fine, but currently overwrites files with duplicate names. How can I avoid this?
<?php
// Configuration - Your Options
$allowed_filetypes = array('.mp3'); // These will be the types of file that will pass the validation.
$max_filesize = 1048576; // Maximum filesize in BYTES (currently 0.5MB).
$upload_path = './uploads/'; // The place the files will be uploaded to (currently a 'files' directory).
$filename = $_FILES['userfile']['name']; // Get the name of the file (including file extension).
$ext = substr($filename, strpos($filename,'.'), strlen($filename)-1); // Get the extension from the filename.
if(!in_array($ext,$allowed_filetypes))
header('Location: http://www.website.com/five/error');
if(filesize($_FILES['userfile']['tmp_name']) > $max_filesize)
header('Location: http://www.website.com/five/error');
if(!is_writable($upload_path))
header('Location: http://www.website.com/five/error');
if(move_uploaded_file($_FILES['userfile']['tmp_name'],$upload_path . $filename))
// echo 'Your file upload was successful, view the file here'; // It worked.
header('Location: http://www.website.com/five/sent');
else
header('Location: http://www.website.com/five/error');
?>

Add an if (file_exists($upload_path.$filename)) before you upload, and set $filename to something else if it does.

One option is to use a database to store the original names, along with a unique id.
Then you can save the file as whatever unique name you want.
Use just the id... file = 1
Use the id and extension = 1.mp3
Use a combination of the id and name = 1_name_of_file.mp3
Or any other unique naming option.
Then you use php to serve the files. Setting the header with the original file name.
Users would not be aware of how you are storing the file. Multiple files with the same name could be uploaded and downloaded with that name, but stored uniquely on the server.
<?php
$actualFile = './uploads/'.$id;
// Can use some smarts to determine the mime type here
header('Content-type: application/force-download');
// The user will be prompted to save it as the filename given here.
header('Content-Disposition: attachment; filename="'.$originalName.'"');
header("Content-Length: ".filesize($actualFile));
// The actual file on the server
readfile($actualFile);
?>
There are also a number of other header options you can set for caching and so on.
http://php.net/manual/en/function.header.php
Fileinfo is a good extension for determining mimetypes
http://www.php.net/manual/en/function.finfo-file.php

Use your own filename instead of the original.
This could for example be generated via uniqid()

store the file with a hash as the name, and store the original filename in a database/flat file along with the hash.
if you want to use flat files it could just be that the user file gets stored as .dat and the info about the file is stored as either .json or .xml or even just a serialized PHP object if you prefer.
also, your file-extension detecting logic fails as soon as the filename has more than one dot (period) in the name.

Related

using php to check if a file is a kml file

I am using move_file_upload on my server side in php so that client can allow users to upload their kml files to the server. I know that in order to check an image , in php I can use $check = getimagesize(file) but what would be the equivalent for a kml file check ?
I donot want to just check the extension of the file. I wish to know if infact the file is a valid kml file or not. If I only check the extension, someone can just post some other malicious file and change its extension to .kml
If you want to see if the file has the extension KML, you can use:
$filename = $_FILES["file"]["name"]; //or however you are getting the filename
$ext = end((explode(".",$filename)));
if($ext!="kml"){
//Extension is incorrect
}
Checking mime content can be helpful.
I am not quite sure what is the correct mime name of kml files but at least with checking in google it should be something as:
mime_content_type ($file) === 'application/vnd.google-earth.kml+xml'
How ever its possible that there are mimes set to 'application/xml' or 'text/xml' so extension validation is required as well ..

Encoding uploaded files name

I want to ask, if I have a web form, and people use it to upload something to my db. If 2 persons uploaded the same file with the same name, in the uploads directory, one of them will replace the other. So I need to change the name of every file uploaded to db to : filename201405051200.pdf or jpg or...
Here we have the filename per example image1 and the numbers are the date and time of the uploads. So any help. I am using the code shown as an answer in the link below:
Uploading blob files/images into Mysql
I used this code:
$path = "../uploads/".$_FILES['file']['name'];
if(move_uploaded_file($_FILES["file"]["tmp_name"], $path.'_'.time())){
...
}
but now the format type of the file is replaced by the time. So if it is img.jpg it is now img85890338jpg and wont open properly
You can use pathinfo to extract the file extension:
$fileExt = pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION);
After that you can create your new file name:
if(move_uploaded_file($_FILES["file"]["tmp_name"], $path.'_'.time().date().'.'.$fileExt)) {
}

Should uploaded files be renamed?

I've been reading up on PHP file upload security and a few articles have recommended renaming the files. For example, the OWASP article Unrestricted File Upload
says:
It is recommended to use an algorithm to determine the filenames. For
instance, a filename can be a MD5 hash of the name of file plus the
date of the day.
If a user uploads a file named Cake Recipe.doc is there really any reason to rename it to 45706365b7d5b1f35?
If the answer is yes, for whatever reason, then how do you keep track of the original file name and extension?
To your primary question, is it good practice to rename files, the answer is a definite yes, especially if you are creating a form of File Repository where users upload files (and filenames) of their choosing, for several reason:
Security - if you have a poorly written application that allows the download of files by name or through direct access (it's a horrid, but it happens), it's much harder for a user, whether maliciously or on purpose, to "guess" the names of files.
Uniqueness -- the likelihood of two different people uploading a file of the same name is very high (ie. avatar.gif, readme.txt, video.avi, etc). The use of a unique identifier significantly decreases the likelihood that two files will be of the same name.
Versioning -- It is much easier to keep multiple "versions" of a document using unique names. It also avoids the need for additional code to parse a filename to make changes. A simple example would document.pdf to document(1).pdf, which becomes more complicated when you don't underestimate users abilities to create horrible names for things.
Length -- working with known filename lengths is always better than working with unknown filename lengths. I can always know that (my filepath) + (X letters) is a certain length, where (my filepath) + (random user filename) is completely unknown.
OS -- the length above can also create problems when attempting to write extremely random/long filenames to a drive. You have to account for special characters, lengths and the concerns for trimmed filenames (user may not receive a working file because the extension has been trimmed).
Execution -- It's easy for the OS to execute a file named .exe, or .php, or (insert other extension). It's hard when there isn't an extension.
URL encoding -- Ensuring the name is URL safe. Cake Recipe.doc is not a URL safe name, and can on some systems (either server or browser side) / some situations, cause inconsistencies when the name should be a urlencoded value.
As for storing the information, you would typically do this in a database, no different than the need you have already, since you need a way to refer back to the file (who uploaded, what the name is, occassionally where it is stored, the time of upload, sometimes the size). You're simply adding to that the actual stored name of the file in addition to the user's name for the file.
The OWASP recommendation isn't a bad one -- using the filename and a timestamp (not date) would be mostly unique. I take it a step further to include the microtime with the timestamp, and often some other unique bit of information, so that a duplicate upload of a small file couldn't occur in the same timeframe -- I also store the date of the upload which is additional insurance against md5 clashes, which has a higher probability in systems that store many files and for years. It is incredibly unlikely that you would generate two like md5s, using filename and microtime, on the same day. An example would be:
$filename = date('Ymd') . '_' . md5($uploaded_filename . microtime());
My 2 cents.
When I upload files I use PHP's unique_id() function for the filename that is stored on the server (and I preserve the file extension since it makes it easier for me when I am looking at all the files in the storage directory via the local file system).
I save the file outside of the website file system (aka you can never browse directly to the files).
I always use php's move_uploaded_file() function to save the file to the server.
I store the original filename, the path/filename where it is stored, and any other project related information you might need about who uploaded it, etc in a database.
In some of my implementations I also create a hash of the file contents and save that in the database too. Then with other uploaded files look in the database to see if I have a copy of that exact file already stored.
Some code examples:
The form:
form method="post" enctype="multipart/form-data" action="your_form_handler.php">
<input type="file" name="file1" value="" />
<input type="submit" name="b1" value="Upload File" />
</form>
The form handler:
<?php
// pass the file input name used in the form and any other pertinent info to store in the db, username in this example
_process_uploaded_file('file1', 'jsmith');
exit;
function _process_uploaded_file($file_key, $username='guest'){
if(array_key_exists($file_key, $_FILES)){
$file = $_FILES[$file_key];
if($file['size'] > 0){
$data_storage_path = '/path/to/file/storage/directory/';
$original_filename = $file['name'];
$file_basename = substr($original_filename, 0, strripos($original_filename, '.')); // strip extention
$file_ext = substr($original_filename, strripos($original_filename, '.'));
$file_md5_hash = md5_file($file['tmp_name']);
$stored_filename = uniqid();
$stored_filename .= $file_ext;
if(! move_uploaded_file($file['tmp_name'], $data_storage_path.$stored_filename)){
// unable to move, check error_log for details
return 0;
}
// insert a record into your db using your own mechanism ...
// $statement = "INSERT into yourtable (original_filename, stored_filename, file_md5_hash, username, activity_date) VALUES (?, ?, ?, ?, NOW())";
// success, all done
return 1;
}
}
return 0;
}
?>
Program to handle download requests
<?php
// Do all neccessary security checks etc to make sure the user is allowed to download the file, etc..
//
$file = '/path/to/your/storage/directory' . 'the_stored_filename';
$filesize = filesize($file);
header('Content-Description: File Transfer');
header("Content-type: application/forcedownload");
header("Content-disposition: attachment; filename=\"filename_to_display.example\"");
header("Content-Transfer-Encoding: Binary");
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header("Content-length: ".$filesize);
ob_clean();
flush();
readfile("$file");
exit;
If you want to present the download in the same page that the user is requesting it from then look at my answer to this post: Dowloading multiple PDF files from javascript
There is a good reason you need to rename uploaded file and it is,
if two upload same file, or files with same name, the latter file will replace the former file which is not favourable.
you can use hashing algos like
$extensions = explode(".",$file-name);
$ext = $extensions[count($extensions)-1];
$file-name = md5($file-name .$_SERVER['REMOTE_ADDR']) .'.' .$ext;
then you can save details of filename, hashed filename, uploader details, date, time to keep track of files

PHP file uploading security

I use file extension for validate uploading file like word excel pdf etc.?
But if user change their file extension then they can upload any file they want.
I want to function that check type of file if user change their file extension after that they should not be able to upload file.
Can any one help
You should also check the mimetypes, for example:
$allowedMimes = array('image/gif', 'image/jpeg', 'image/jpg', 'image/png', 'image/bmp', 'image/wbmp');
//getting the mime type (it can be different from the extension) Be careful!
$imgInfo = getimagesize(imagePath);
$type = strtolower($imgInfo['mime']);
//hey dude!! This is a fake image!!
if(!in_array($type, $allowedMimes)){
//We delete it!!
unlink(imagePath);
}else{
//do whatever with the image...
}
You can find more info about mime types here.
To be safe
Move all the files regardless of type out of the webroot.
Dont allow direct access to the file, use a loader to send the file
to the user if you have a download feature.
Force the download
Have a script, download.php or whatever, get the file's ID, verify who is logged in, and if everything checks out, fetch the file, read it out to the browser, and send the appropriate download headers.
header('Content-type: application/octet-stream');
header('Content-disposition: attachment; filename=file.ext');
header("Content-Length: " . filesize('../not_in_web_root/file.ext'));
header("Content-Transfer-Encoding: binary");
readfile('../not_in_web_root/file.ext');
exit;
Only accept files you want to accept by checking the extension and mimetype where possible. Its
even ok to even accept php as long as you dont allow it to execute or give user direct access to it.
If your only allowing images then use a function like getimagesize(), if it has a size its an image, but still dont allow direct access to it as PHP maybe embedded into it.
If you offer a filesystem feature to your users, make it a virtual one, based on values within a database not access to the real files.
You could possibly look at the mime type of the file? http://us2.php.net/manual/en/fileinfo.constants.php

What is the most secure method to upload image file to server with PHP?

I know this topic is widely talked about. I've done my research, and decided to store image files onto the server instead of the DB as a blob file. The problem that I am having is trying to figure out the best way to upload the file, store it in a designated folder then storing the destination in the db..say../img/something.jpg - I've tried looking through many resources but most of them are missing some important steps in the process.
Problems:
Finding a secure way for uploading the img file
Limiting the file
size Uploading the image to a destination file
Storing the destination file as a text in the DB
I'm using PHP and MySQL.
Dunno what all your points about, but what you really have to be concerned with is
check for the file extension.
extract it from the filename and compare with allowed ones.
also it would be good to check filename to have only one dot, or at least it doesn't have a name like name.html.jpg, due to some odd Apache behavior.
check for the file contents. the best way would be to create a brand new image out of the uploaded one.
take usual precautions while working with DB.
Here you go, this covers the basic ideas of what you want to do:
<?php
$allowedTypes = array("image/jpg", "image/jpeg", "image/png");
$maxSize = 3 * 1024 * 1024; // 3Mb
$fileType = $_FILES["file"]["type"];
$fileSize = $_FILES["file"]["size"];
// check if there was an error
if ($_FILES["file"]["error"] > 0)
{
die($_FILES["file"]["error"]);
}
// check if the filetype is valid
if (!in_array($fileType, $allowedTypes))
{
die("Invalid file type: $fileType");
}
// check if the size doesn't exceed the limitations
if ($fileSize > $maxSize)
{
die("The file was too big: $fileSize");
}
$name = $_FILES["file"]["name"];
$tmpfile = $_FILES["file"]["tmp_name"];
// check if the filename is valid
if (preg_match("/[\w-]+\.(jpg|jpeg|png)$/", $name) != 1)
{
die("Invalid file name: $name");
}
// create unique name if needed
$path = "/var/www/images/" . $name;
move_uploaded_file($tmpfile, $path);
// add the filepath to mysql
mysql_connect("localhost", "username", "password");
mysql_select_db("imagedb");
mysql_query("INSERT INTO images (Location, Size) VALUES ('$path', '$size');");
?>
This is meant to show how it could be done.
read this
personally I'd use imgur which is used here on stackexchange websites

Categories