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 |
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.
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 |
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 |
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 |
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
It this script vulnerable to Shell uploading?