Friday, December 26, 2008

Secure download-upload script in Php

Recently I have written a blog post on how to insert a file in mysql database as a blob data type. In this post I will explain how you can upload user submitted file securely to your server and make it available for download. I personally prefer this method as work load on a file system will be better than on a database system. But we'll still be using a mysql database to record details of an uploaded file. For a more visited site this database should also be recording user's ip and browser etc. But we'll just go through with a simple database schema. Rest is upto you.

So Our schema goes like this,

First create the table mydata by executing belowgiven query:

CREATE TABLE `mydb`.`mydata` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`save_name` VARCHAR( 255 ) NOT NULL ,
`orig_name` VARCHAR( 255 ) NOT NULL ,
`upload_date` DATETIME NOT NULL ,
`download_count` INT NOT NULL DEFAULT '0'
) ENGINE = InnoDB

save_name is the name of the file with which it will be saved on your web site's server.
orig_name is the actual name of the file with which the user will download and have uploaded it.
We are keeping it with a different name to keep away from name ambiguity in case two users submit file with same name ... and even we don't want people on the server to interfere with file .. to keep user's file secure..
We'll also keep track of Date of upload and how many times it has been downloaded.

Let us start our upload file php code.

upload_file.php

<?php
if ( isset($_POST['submit']))
{
$upload_dir = "uploads/";
if ( $_FILES['file']['error'] === 0 )
{
//Here is the place where you can check for your desired file extension eg jpg,zip etc
//For this example i am assuming any type of file can be uploaded

//A unique 32 char name for the uploaded file
$file_name = md5(uniqid(microtime(),true));

//Upload the file there is no error and type is also fine
if ( move_uploaded_file($_FILES['file']['tmp_name'],$upload_dir.$file_name))
{
$conn = mysql_connect("localhost","root","");
mysql_select_db("mydb",$conn);
$query = "insert into mydata (save_name,orig_name,save_date)
VALUES('{$file_name}','{$_FILES['file']['name']}',NOW())";
if (mysql_query($query,$conn))
echo "File added successfully";
else
echo "Query error";
mysql_close($conn);
}
else
echo "File upload error";
}
else
{
echo "File upload error : {$_FILES['file']['error']}";
}
}
?>

Before using this script you'll have to create the "uploads" directory in the same directory where this script is saved.

Now we come to the download part.

download_file.php
<?php
$id = (int)$_GET['id'];
$upload_dir = "uploads/";
$conn = mysql_connect("localhost","root","");
mysql_select_db("mydb",$conn);
$query = "select * from mydata where id = {$id}";
if ( $result = mysql_query($query,$conn) )
{
if ( $row = mysql_fetch_array($result) )
{
$query = "update mydata set download_count = download_count + 1
where id = {$id}";
mysql_query($query,$conn);
$file_name = $upload_dir.$row['save_name'];
$file = file_get_contents($file_name);
$size = filesize($file_name);
$orig_name = $row['orig_name'];
header("Content-Type: application/octet-stream");
header("Content-Disposition: inline; filename={$orig_name}");
header("Content-Length: {$size}");
echo $file;
}
else
echo "No file found with given id";
}
else
echo "error in query";
?>

A secure file upload/download script is ready now. You'll have to take care of the allowable types which I haven't cater for the example and ensure that when a user enter uploads directory url in his browser he should get a 403 error ie set appropriate permissions. Ok thats all now u are secure :)

2 comments:

  1. nice sharing brother good work. Just few weeks back in one of my recent projects I created a php class for uploading stuff with many security as well as image and csv manipulation features. I'll try sharing it over my blog so do visit and check that out as well.

    ReplyDelete