This function is defined in functions_external.php:
function file_upload_check_1($file, $file_extensions = array("asp", "aspx", "dll", "exe", "jsp", "php"), $directory = "images")
{ $file_error ="";// Checks if the input field is emptyif($file["name"] =="") { $file_error ="Please select a file...";return $file_error; }// Checks if there is an error with the fileswitch($file["error"]) // URL: http://php.net/manual/en/features.file-upload.errors.php {case1 : $file_error ="Sorry, the file is too large. Please try again...";break;case2 : $file_error ="Sorry, the file is too large. Please try again...";break;case3 : $file_error ="Sorry, the file was only partially uploaded. Please try again...";break;case6 : $file_error ="Sorry, a temporary folder is missing. Please try again...";break;case7 : $file_error ="Sorry, the file could not be written. Please try again...";break;case8 : $file_error ="Sorry, a PHP extension stopped the file upload. Please try again...";break; }if($file_error) {return $file_error; }// Breaks the file in pieces (.) All pieces are put in an array $file_array =explode(".", $file["name"]);// Puts the last part of the array (= the file extension) in a new variabele// Converts the characters to lower case $file_extension =strtolower($file_array[count($file_array)-1]);// Searches if the file extension exists in the 'allowed' file extensions array if(in_array($file_extension, $file_extensions)) { $file_error = "Sorry, the file extension is not allowed. The following extensions are blocked: <b>" . join(", ", $file_extensions) . "</b>";
return $file_error; }// Checks if the file already exists in the directoryif(is_file("$directory/". $file["name"])) { $file_error ="Sorry, the file already exists. Please rename the file..."; }return $file_error;}
This implementation uses blacklist to filter out unwanted file extensions. Specifically, the following file extensions are blocked:
asp
aspx
dll
exe
jsp
php
Of course this implementation is not sufficient at all. For example, we can try things like php3/php4/php5 as file extension and bypass the check.
Security Level 2
In this level, the filter used is called file_upload_check_2:
This function is defined in functions_external.php:
functionfile_upload_check_2($file, $file_extensions =array("jpeg", "jpg", "png", "gif"), $directory ="images"){ $file_error ="";// Checks if the input field is emptyif($file["name"] =="") { $file_error ="Please select a file...";return $file_error; }// Checks if there is an error with the fileswitch($file["error"]) // URL: http://php.net/manual/en/features.file-upload.errors.php {case1 : $file_error ="Sorry, the file is too large. Please try again...";break;case2 : $file_error ="Sorry, the file is too large. Please try again...";break;case3 : $file_error ="Sorry, the file was only partially uploaded. Please try again...";break;case6 : $file_error ="Sorry, a temporary folder is missing. Please try again...";break;case7 : $file_error ="Sorry, the file could not be written. Please try again...";break;case8 : $file_error ="Sorry, a PHP extension stopped the file upload. Please try again...";break; }if($file_error) {return $file_error; }// Breaks the file in pieces (.) All pieces are put in an array $file_array =explode(".", $file["name"]);// Puts the last part of the array (= the file extension) in a new variabele// Converts the characters to lower case $file_extension =strtolower($file_array[count($file_array)-1]);// Searches if the file extension exists in the 'allowed' file extensions array if(!in_array($file_extension, $file_extensions)) { $file_error = "Sorry, the file extension is not allowed. Only the following extensions are allowed: <b>" . join(", ", $file_extensions) . "</b>";
return $file_error; }// Checks if the file already exists in the directoryif(is_file("$directory/". $file["name"])) { $file_error ="Sorry, the file already exists. Please rename the file..."; }return $file_error;}
This implementation uses whitelist to only allow wanted file extensions. Specifically, the following file extensions are accepted:
jpeg
jpg
png
gif
This implementation looks sufficient at first, but if you dig deeper into the function calls, you will find this line of code:
The nuance is that the in_array() function takes 3 arguments but this code only used 2. The third argument bool $strict is set to false by default, which indicates loose comparison. PHP loose comparison is a weird "feature" and it is the source of many dumb vulnerabilities. For example:
You may think the output will be bool(false), but no, the output is bool(true)! This is because in loose comparison, we have 0 == "apple". In fact, every string started with letter is evalated to 0 in loose comparison. I am speechless.