add a note add a note

User Contributed Notes 32 notes

up
276
CertaiN
4 years ago
You'd better check $_FILES structure and values throughly.
The following code cannot cause any errors absolutely.

Example:
<?php

header
('Content-Type: text/plain; charset=utf-8');

try {
   
   
// Undefined | Multiple Files | $_FILES Corruption Attack
    // If this request falls under any of them, treat it invalid.
   
if (
        !isset(
$_FILES['upfile']['error']) ||
       
is_array($_FILES['upfile']['error'])
    ) {
        throw new
RuntimeException('Invalid parameters.');
    }

   
// Check $_FILES['upfile']['error'] value.
   
switch ($_FILES['upfile']['error']) {
        case
UPLOAD_ERR_OK:
            break;
        case
UPLOAD_ERR_NO_FILE:
            throw new
RuntimeException('No file sent.');
        case
UPLOAD_ERR_INI_SIZE:
        case
UPLOAD_ERR_FORM_SIZE:
            throw new
RuntimeException('Exceeded filesize limit.');
        default:
            throw new
RuntimeException('Unknown errors.');
    }

   
// You should also check filesize here.
   
if ($_FILES['upfile']['size'] > 1000000) {
        throw new
RuntimeException('Exceeded filesize limit.');
    }

   
// DO NOT TRUST $_FILES['upfile']['mime'] VALUE !!
    // Check MIME Type by yourself.
   
$finfo = new finfo(FILEINFO_MIME_TYPE);
    if (
false === $ext = array_search(
       
$finfo->file($_FILES['upfile']['tmp_name']),
        array(
           
'jpg' => 'image/jpeg',
           
'png' => 'image/png',
           
'gif' => 'image/gif',
        ),
       
true
   
)) {
        throw new
RuntimeException('Invalid file format.');
    }

   
// You should name it uniquely.
    // DO NOT USE $_FILES['upfile']['name'] WITHOUT ANY VALIDATION !!
    // On this example, obtain safe unique name from its binary data.
   
if (!move_uploaded_file(
       
$_FILES['upfile']['tmp_name'],
       
sprintf('./uploads/%s.%s',
           
sha1_file($_FILES['upfile']['tmp_name']),
           
$ext
       
)
    )) {
        throw new
RuntimeException('Failed to move uploaded file.');
    }

    echo
'File is uploaded successfully.';

} catch (
RuntimeException $e) {

    echo
$e->getMessage();

}

?>
up
19
xmontero at dsitelecom dot com
6 years ago
If "large files" (ie: 50 or 100 MB) fail, check this:

It may happen that your outgoing connection to the server is slow, and it may timeout not the "execution time" but the "input time", which for example in our system defaulted to 60s. In our case a large upload could take 1 or 2 hours.

Additionally we had "session settings" that should be preserved after upload.

1) You might want review those ini entries:

* session.gc_maxlifetime
* max_input_time
* max_execution_time
* upload_max_filesize
* post_max_size

2) Still fails? Caution, not all are changeable from the script itself. ini_set() might fail to override.

More info here:
http://www.php.net/manual/es/ini.list.php

You can see that the "upload_max_filesize", among others, is PHP_INI_PERDIR and not PHP_INI_ALL. This invalidates to use ini_set():
http://www.php.net/manual/en/configuration.changes.modes.php

Use .htaccess instead.

3) Still fails?. Just make sure you enabled ".htaccess" to overwrite your php settings. This is made in the apache file. You need at least AllowOverride Options.

See this here:
http://www.php.net/manual/en/configuration.changes.php

You will necessarily allow this manually in the case your master files come with AllowOverride None.

Conclussion:

Depending on the system, to allow "large file uploads" you must go up and up and up and touch your config necessarily up to the apache config.

Sample files:

These work for me, for 100MB uploads, lasting 2 hours:

In apache-virtual-host:
-----------------------------------------------------------
<Directory /var/www/MyProgram>
    AllowOverride Options
</Directory>
-----------------------------------------------------------

In .htaccess:
-----------------------------------------------------------
php_value session.gc_maxlifetime 10800
php_value max_input_time         10800
php_value max_execution_time     10800
php_value upload_max_filesize    110M
php_value post_max_size          120M
-----------------------------------------------------------

In the example,
- As I last 1 to 2 hours, I allow 3 hours (3600x3)
- As I need 100MB, I allow air above for the file (110M) and a bit more for the whole post (120M).
up
14
jan at lanteraudio dot nl
5 years ago
Also stumbled on the max_file_size problem, in particular getting no response, no error whatsoever when uploading a file bigger than the set upload_max_filesize.

I found that it's not the upload_max_filesize setting, but instead the post_max_size setting causing this no response issue. So if you set post_max_size way larger than upload_max_filesize, at least you are likely to get an error response when filesize exceeds upload_max_filesize but is still within the limits of post_max_size.

Hope this helps anyone.
up
11
ceo at l-i-e dot com
13 years ago
Using /var/www/uploads in the example code is just criminal, imnsho.

One should *NOT* upload untrusted files into your web tree, on any server.

Nor should any directory within your web tree have permissions sufficient for an upload to succeed, on a shared server. Any other user on that shared server could write a PHP script to dump anything they want in there!

The $_FILES['userfile']['type'] is essentially USELESS.
A. Browsers aren't consistent in their mime-types, so you'll never catch all the possible combinations of types for any given file format.
B. It can be forged, so it's crappy security anyway.

One's code should INSPECT the actual file to see if it looks kosher.

For example, images can quickly and easily be run through imagegetsize and you at least know the first N bytes LOOK like an image.  That doesn't guarantee it's a valid image, but it makes it much less likely to be a workable security breaching file.

For Un*x based servers, one could use exec and 'file' command to see if the Operating System thinks the internal contents seem consistent with the data type you expect.

I've had trouble in the past with reading the '/tmp' file in a file upload.  It would be nice if PHP let me read that file BEFORE I tried to move_uploaded_file on it, but PHP won't, presumably under the assumption that I'd be doing something dangerous to read an untrusted file.  Fine.   One should move the uploaded file to some staging directory.  Then you check out its contents as thoroughly as you can.  THEN, if it seems kosher, move it into a directory outside your web tree.  Any access to that file should be through a PHP script which reads the file.  Putting it into your web tree, even with all the checks you can think of, is just too dangerous, imnsho.

There are more than a few User Contributed notes here with naive (bad) advice.  Be wary.
up
9
jedi_aka at yahoo dot com
11 years ago
For those of you trying to make the upload work with IIS on windows XP/2000/XP Media and alike here is a quick todo.

1) Once you have created subdirectories "uploads/"  in the same directory wher you code is running use the code from oportocala above and to make absolutely sure sure that the file you are trying to right is written under that folder. ( I recomend printing it using echo $uploadfile; )

2) In windows explorer browse to the upload directory created above and share it. To do that execute the following substeps.
     a) Right click the folder click "sharing and security..."
     b) Check 'Share this folder on the network'
     c) Check 'Allow network users to change my files' ( THIS STEP IS VERY IMPORTANT )
     d) click 'ok' or 'apply'

3) you can then go in the IIS to set read and write permissions for it. To do that execute the followin substeps.
     a) Open IIS (Start/Controp Panel (classic View)/ Admistrative tools/Internet Information Service
     b) Browse to your folder (the one we created above)
     c) right click and select properties.
     d) in the Directory tab, make sure, READ, WRITE, AND DIRECTORY BROWSING are checked.
     e) For the security freaks out there, You should also make sure that 'execute permissions:' are set to Script only or lower (DO NOT SET IT TO 'script and executable)'( that is because someone could upload a script to your directory and run it. And, boy, you do not want that to happen).

there U go.

Send me feed back it if worked for you or not so that I can update the todo.

jedi_aka@yahoo.com

PS: BIG thanks to oportocala
up
5
myko AT blue needle DOT com
13 years ago
Just a quick note that there's an issue with Apache, the MAX_FILE_SIZE hidden form field, and zlib.output_compression = On.  Seems that the browser continues to post up the entire file, even though PHP throws the MAX_FILE_SIZE error properly.  Turning zlib compression to OFF seems to solve the issue.  Don't have time to dig in and see who's at fault, but wanted to save others the hassle of banging their head on this one.
up
11
svenr at selfhtml dot org
11 years ago
Clarification on the MAX_FILE_SIZE hidden form field:

PHP has the somewhat strange feature of checking multiple "maximum file sizes".

The two widely known limits are the php.ini settings "post_max_size" and "upload_max_size", which in combination impose a hard limit on the maximum amount of data that can be received.

In addition to this PHP somehow got implemented a soft limit feature. It checks the existance of a form field names "max_file_size" (upper case is also OK), which should contain an integer with the maximum number of bytes allowed. If the uploaded file is bigger than the integer in this field, PHP disallows this upload and presents an error code in the $_FILES-Array.

The PHP documentation also makes (or made - see bug #40387 - http://bugs.php.net/bug.php?id=40387) vague references to "allows browsers to check the file size before uploading". This, however, is not true and has never been. Up til today there has never been a RFC proposing the usage of such named form field, nor has there been a browser actually checking its existance or content, or preventing anything. The PHP documentation implies that a browser may alert the user that his upload is too big - this is simply wrong.

Please note that using this PHP feature is not a good idea. A form field can easily be changed by the client. If you have to check the size of a file, do it conventionally within your script, using a script-defined integer, not an arbitrary number you got from the HTTP client (which always must be mistrusted from a security standpoint).
up
7
~caetin~ ( at ) ~hotpop~ ( dot ) ~com~
14 years ago
From the manual:

     If no file is selected for upload in your form, PHP will return $_FILES['userfile']['size'] as 0, and $_FILES['userfile']['tmp_name'] as none.

As of PHP 4.2.0, the "none" is no longer a reliable determinant of no file uploaded. It's documented if you click on the "error codes" link, but you need to look at the $_FILES['your_file']['error']. If it's 4, then no file was selected.
up
5
Rob
10 years ago
You should not have any directories within your website root that has the permissions required for file upload.  If you are going to do a file upload, I recommend you use the PHP FTP Functions in conjunction with your file field, that way the files are transferred to a remote FTP location separate from your server.