Php hidden file upload

How to upload files with PHP correctly and securely

If you just want the sourcecode — scroll to the end of the page or click here. But I recommend reading the article to understand why I’m doing things as I do and how it works. Hey Guys, in this post, I’ll show you how to upload files to your server using HTML and PHP and validate the files. I hope it’s useful for some of you and now happy coding 🙂

Security information

First of all, the most important thing I want to tell you, the $_FILES variable in PHP (except tmp_name ) can be modified. That means, do not check e.g. the filesize with $_FILES[‘myFile’][‘size’] , because this can be modified by the uploader in case of an attack. In other words, when you validate the upload with this method, attackers can pretend that their file has another file size or type. As you can see, there is a lot we need to take care of. Maybe it’s worth considering to use an already existing service. With Uploadcare you can upload and manage files quickly and easily via their PHP integration. So, let’s move on and create our own, secure, file upload.

HTML Setup

 method="post" action="upload.php" enctype="multipart/form-data">  type="file" name="myFile" />  type="submit" value="Upload">  

That’s it. Note the action=»upload.php» , that’s the PHP script handling the upload. And we use the name myFile to identify the file in PHP.

PHP Validation

Now, let’s validate the file in the upload.php file. First of all, we have to check if there is a file passed to our script. We do this using the $_FILES variable:

if (!isset($_FILES["myFile"]))  die("There is no file to upload."); > 

But remember, for security reasons, we can’t get the filesize using $_FILES . When the user uploads the file, PHP stores it temporarily and you can get the path using $_FILES[‘myFile’][‘tmp_name’] . That’s what we use now to get the real size and type of the file.

$filepath = $_FILES['myFile']['tmp_name']; $fileSize = filesize($filepath); $fileinfo = finfo_open(FILEINFO_MIME_TYPE); $filetype = finfo_file($fileinfo, $filepath); 

Now we have the real information, let’s validate the filesize. We don’t want to allow users to upload empty files, so first, we check if the file size is greater than 0:

if ($fileSize === 0)  die("The file is empty."); > 
if ($fileSize > 3145728)  // 3 MB (1 byte * 1024 * 1024 * 3 (for 3 MB)) die("The file is too large"); > 

Great. But you’ll usually only allow specific types to be uploaded, e.g. .png or .jpg for profile images. For more flexibility, let’s create an array with all allowed file types:
(Thanks to Gary Marriott and Renorram Brandão for pointing me out, we have to store the extensions for each type here in the array so we can append it later to the filename)

$allowedTypes = [ 'image/png' => 'png', 'image/jpeg' => 'jpg' ]; 

You can find a list of MIME-Types here (It’s in german, but there is a great table with all MIME-Types and file extensions). Now let’s check if the type of the file is allowed:

if(!in_array($filetype, array_keys($allowedTypes)))  die("File not allowed."); > 

And we’re done with validating! In the last step, we move the file to our uploads directory (or wherever you want to). For this, I define a variable with my target directory, then grab the current filename and extension and build the new, target file path:

$filename = basename($filepath); // I'm using the original name here, but you can also change the name of the file here $extension = $allowedTypes[$filetype]; $targetDirectory = __DIR__ . "/uploads"; // __DIR__ is the directory of the current PHP file $newFilepath = $targetDirectory . "/" . $filename . "." . $extension; 
if (!copy($filepath, $newFilepath ))  // Copy the file, returns false if failed die("Can't move file."); > unlink($filepath); // Delete the temp file echo "File uploaded successfully :)"; 

That’s it! Now you have a secure file upload where you can strictly define which files can be uploaded and which not!

Full code

  lang="en">  charset="UTF-8">  http-equiv="X-UA-Compatible" content="IE=edge">  name="viewport" content="width=device-width, initial-scale=1.0"> Document    method="post" action="upload.php" enctype="multipart/form-data">  type="file" name="myFile" />  type="submit" value="Upload">   
 if (!isset($_FILES["myFile"]))  die("There is no file to upload."); > $filepath = $_FILES['myFile']['tmp_name']; $fileSize = filesize($filepath); $fileinfo = finfo_open(FILEINFO_MIME_TYPE); $filetype = finfo_file($fileinfo, $filepath); if ($fileSize === 0)  die("The file is empty."); > if ($fileSize > 3145728)  // 3 MB (1 byte * 1024 * 1024 * 3 (for 3 MB)) die("The file is too large"); > $allowedTypes = [ 'image/png' => 'png', 'image/jpeg' => 'jpg' ]; if (!in_array($filetype, array_keys($allowedTypes)))  die("File not allowed."); > $filename = basename($filepath); // I'm using the original name here, but you can also change the name of the file here $extension = $allowedTypes[$filetype]; $targetDirectory = __DIR__ . "/uploads"; // __DIR__ is the directory of the current PHP file $newFilepath = $targetDirectory . "/" . $filename . "." . $extension; if (!copy($filepath, $newFilepath))  // Copy the file, returns false if failed die("Can't move file."); > unlink($filepath); // Delete the temp file echo "File uploaded successfully :)"; 

Источник

Php hidden file upload

I think the way an array of attachments works is kind of cumbersome. Usually the PHP guys are right on the money, but this is just counter-intuitive. It should have been more like:

Array
(
[0] => Array
(
[name] => facepalm.jpg
[type] => image/jpeg
[tmp_name] => /tmp/phpn3FmFr
[error] => 0
[size] => 15476
)

Anyways, here is a fuller example than the sparce one in the documentation above:

foreach ( $_FILES [ «attachment» ][ «error» ] as $key => $error )
$tmp_name = $_FILES [ «attachment» ][ «tmp_name» ][ $key ];
if (! $tmp_name ) continue;

$name = basename ( $_FILES [ «attachment» ][ «name» ][ $key ]);

if ( $error == UPLOAD_ERR_OK )
if ( move_uploaded_file ( $tmp_name , «/tmp/» . $name ) )
$uploaded_array [] .= «Uploaded file ‘» . $name . «‘.
\n» ;
else
$errormsg .= «Could not move uploaded file ‘» . $tmp_name . «‘ to ‘» . $name . «‘
\n» ;
>
else $errormsg .= «Upload error. [» . $error . «] on file ‘» . $name . «‘
\n» ;
>
?>

Do not use Coreywelch or Daevid’s way, because their methods can handle only within two-dimensional structure. $_FILES can consist of any hierarchy, such as 3d or 4d structure.

The following example form breaks their codes:

As the solution, you should use PSR-7 based zendframework/zend-diactoros.

use Psr \ Http \ Message \ UploadedFileInterface ;
use Zend \ Diactoros \ ServerRequestFactory ;

$request = ServerRequestFactory :: fromGlobals ();

if ( $request -> getMethod () !== ‘POST’ ) http_response_code ( 405 );
exit( ‘Use POST method.’ );
>

$uploaded_files = $request -> getUploadedFiles ();

if (
!isset( $uploaded_files [ ‘files’ ][ ‘x’ ][ ‘y’ ][ ‘z’ ]) ||
! $uploaded_files [ ‘files’ ][ ‘x’ ][ ‘y’ ][ ‘z’ ] instanceof UploadedFileInterface
) http_response_code ( 400 );
exit( ‘Invalid request body.’ );
>

$file = $uploaded_files [ ‘files’ ][ ‘x’ ][ ‘y’ ][ ‘z’ ];

if ( $file -> getError () !== UPLOAD_ERR_OK ) http_response_code ( 400 );
exit( ‘File uploading failed.’ );
>

$file -> moveTo ( ‘/path/to/new/file’ );

The documentation doesn’t have any details about how the HTML array feature formats the $_FILES array.

Array
(
[document] => Array
(
[name] => sample-file.doc
[type] => application/msword
[tmp_name] => /tmp/path/phpVGCDAJ
[error] => 0
[size] => 0
)
)

Multi-files with HTML array feature —

Array
(
[documents] => Array
(
[name] => Array
(
[0] => sample-file.doc
[1] => sample-file.doc
)

[type] => Array
(
[0] => application/msword
[1] => application/msword
) [tmp_name] => Array
(
[0] => /tmp/path/phpVGCDAJ
[1] => /tmp/path/phpVGCDAJ
)

The problem occurs when you have a form that uses both single file and HTML array feature. The array isn’t normalized and tends to make coding for it really sloppy. I have included a nice method to normalize the $_FILES array.

function normalize_files_array ( $files = [])

foreach( $files as $index => $file )

if (! is_array ( $file [ ‘name’ ])) $normalized_array [ $index ][] = $file ;
continue;
>

foreach( $file [ ‘name’ ] as $idx => $name ) $normalized_array [ $index ][ $idx ] = [
‘name’ => $name ,
‘type’ => $file [ ‘type’ ][ $idx ],
‘tmp_name’ => $file [ ‘tmp_name’ ][ $idx ],
‘error’ => $file [ ‘error’ ][ $idx ],
‘size’ => $file [ ‘size’ ][ $idx ]
];
>

?>

The following is the output from the above method.

Array
(
[document] => Array
(
[0] => Array
(
[name] => sample-file.doc
[type] => application/msword
[tmp_name] => /tmp/path/phpVGCDAJ
[error] => 0
[size] => 0
)

[documents] => Array
(
[0] => Array
(
[name] => sample-file.doc
[type] => application/msword
[tmp_name] => /tmp/path/phpVGCDAJ
[error] => 0
[size] => 0
) [1] => Array
(
[name] => sample-file.doc
[type] => application/msword
[tmp_name] => /tmp/path/phpVGCDAJ
[error] => 0
[size] => 0
)

Источник

Читайте также:  Номер первого вхождения python
Оцените статью