Today's Question:  What does your personal desk look like?        GIVE A SHOUT

File upload in PHP

  sonic0002        2012-11-30 22:01:28       4,469        1    

File is a special kind of form data, when being uploaded to the server through HTTP POST request, PHP will create a $_FILES global array, the relevant file information will be stored in this global array. We will illustrate file upload with some code snippets using PHP and look into the internal work mechanism. Lastly will talk about file upload security.

File upload

In order for users to upload files in client side, we have to provide a form on the user interface. Since the uploaded file is a special data, it is different from other POST data, so we have have to set a special encoding for the form.

1
<form action="upload.php" method="POST" enctype="multipart/form-data">

You may not be very familiar with the enctype attribute above, because we may ignore it frequently. If there are both normal data and file data in the HTTP POST request, this attribute should be added, this can increase the compatibility among various browsers.

Next we need to add an file input element in the form

1
<input type="file" name="attachment" />

The appearance of the above element in different browsers may be different, for most browsers, the above element will be rendered as an input field and a browse button. Hence user can enter the file path manually or select a file by clicking the browse button. However, in Apple's Safari, Google's Chrome, we can only use the browse button mode. You can also define the style of the upload field.

Next we will provide a complete code snippet about how to process file upload.

1
2
3
4
5
<p>Please upload your attachment:</p>
<form action="upload.php" method="POST" enctype="multipart/form-data"> 
<input type="file" name="attachment" /> 
<input type="submit" value="Upload" /> 
</form>

When user clicks the upload button, the HTTP request will be sent to upload.php, we now use upload.php to show all the data in $_FILES global array.

1
2
3
4
5
6


header('Content-Type: text/plain');
print_r($_FILES);

?>

Here is an experiment, if we upload the logo into the upload.php, let's see what upload.php will show.

1
2
3
4
5
6
7
8
9
10
11
12
Array
(
    [attachment] => Array
        (
            [name] => boy.jpg
            [type] => image/jpeg
            [tmp_name] => D:\xampp\tmp\php1168.tmp
            [error] => 0
            [size] => 11490
        )

)

There will be all information related to the uploaded file. However, how can we ensure the security of the information, how do we know whether the name is modified or not? We need to be careful about the data from client.

HTTP request details

To better understand file upload, we have to check what kind of data is sent to the server. Now let's upload a test.txt file.

The output in upload.php is :

1
2
3
4
5
6
7
8
9
10
11
12
Array
(
    [attachment] => Array
        (
            [name] => test.txt
            [type] => text/plain
            [tmp_name] => D:\xampp\tmp\php51C0.tmp
            [error] => 0
            [size] => 40
        )

)

And let's look at some more data

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
POST /upload.php HTTP/1.1
Host: www.360weboy.me
Referer: http://www.360weboy.me/
multipart/form-data; boundary=---------------------------24464570528145
Content-Length: 234
 
-----------------------------24464570528145 
Content-Disposition: form-data; name="attachment"; filename="test.txt" 
Content-Type: text/plain 

360weboy 

360days 

Life Of A Web Boy 
-----------------------------24464570528145--

We need to pay attention to some strings in above data sent to server, they are name, filename and Content-Type, they are attachment, test.txt and text/plain respectively in the above example. After that, it's the actual file content to be uploaded.

Security

In order to secure the uploaded file, we need to check tmp_name and size in $_FILES To ensure the file pointed by tmp_name is the file uploaded on the client side, but not some /etc/passwd files, we can use is_uploaded_file() to check it.

1
2
3
4
5
6
7
8
9

 
$filename = $_FILES['attachment']['tmp_name'];
 
if (is_uploaded_file($filename)) { 
    /* It's an uploaded file. */
}
 
?>

In addition, we need to check the file's mime-type, i,e the type in the output of upload.php. We uploaded a logo in the first example, the value of $_FILES['attachment']['type'] is "image/jpeg", if we only allow image/png, image/jpeg,image/gif,image/x-png and image/p-jpeg images, we can use below code snippet to check.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$allow_mimes = array(
    'image/png',
    'image/x-png',
    'image/gif',
    'image/jpeg',
    'image/pjpeg'
);

$image = $_FILES['attachment'];

if(!in_array($image['type'], $allow_mimes)) {
    die('对不起, 你上传的文件格式不准确;我们只接受图片文件.');
}

// Continue to process image file

We have ensured the uploaded files are what the server expects. This is not enough, since the mime-type can be hacked, for example, if some user creates a jpg image and inserted in some malicious PHP codes in the image. When this file is uploaded, it will pass through the check of mime-type, it will be considered as an image and the malicious codes will be executed.

We have to check the file extension as well.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$allow_mimes = array(
    'image/png' => '.png',
    'image/x-png' => '.png',
    'image/gif' => '.gif',
    'image/jpeg' => '.jpg',
    'image/pjpeg' => '.jpg'
);

$image = $_FILES['attachment'];

if(!array_key_exists($image['type'], $allow_mimes )) {
    die('对不起, 你上传的文件格式不准确;我们只接受图片文件.');
}

// Get file name
$filename = substr($image['name'], 0, strrpos($image['name'], '.'));

// Append extension
$filename .= $allow_mimes[$image['type']];

// Continue to process image file

After checking files, we can use move_uploaded_file() to save the file to specified path on the server.

1
2
3
4
5
6
7
8
9
10

 
$tmp_filename = $_FILES['attachment']['tmp_name'];
$filename = '/path/to/attachment.txt';
 
if (move_uploaded_file(tmp_filename, $filename)) { 
    /* $temp_filename 保存在临时目录中的上传文件, 然后成功将其保存到对应目录下的attachment.txt文件中. */
}
 
?>

If you want to limit the filesize of the uploaded file, you can use the filesize() to get the size of the file.

You can also refer this article about AJAX file upload : AJAX file upload tutorial

Source : http://www.360weboy.com/php/upload.html

PHP  FILE UPLOAD 

Share on Facebook  Share on Twitter  Share on Weibo  Share on Reddit 

  RELATED


  1 COMMENT


Saquib Rizwan [Reply]@ 2017-05-04 06:01:19

It this script vulnerable to Shell uploading?