I've put together an example single file upload script that attempts to cover all the things PHP could check for prior to allowing a successful file upload. Is there anything else maybe now available in PHP 7.4+ I could use to make this more secure? For example, I use filter_input
below even though I don't find it in many scripts out there.
Take a look
<?php # EVALUATE REQUEST METHOD $REQUEST_METHOD = filter_input(INPUT_SERVER, 'REQUEST_METHOD', FILTER_SANITIZE_ENCODED); switch ($REQUEST_METHOD) { # HTTP:POST - PAYLOAD:BLOB case 'POST': # POST IMAGE if(\in_array(@$_FILES["files"], $_FILES) && \count($_FILES) === 1) { upload(); } break; default: methodInvalid(); break; } /** * Function upload() uploads a single file. * * */ function upload() { // Establish the upload file directory $upload_dir = $_SERVER['DOCUMENT_ROOT'] . '/gui/v1/uploads/submittals/'; // Establish the upload file path $upload_file = $upload_dir . $_FILES['files']['name'][0]; // Derive the upload file extension $upload_file_extension = strtolower(pathinfo($upload_file, PATHINFO_EXTENSION)); // Allowed file types // $allowed_file_extensions = ['pdf', 'jpg', 'jpeg', 'png', 'gif']; $allowed_file_extensions = ['pdf']; /** * Does tmp file exist? * * */ if (!file_exists($_FILES['files']['tmp_name'][0])) { # ERROR object $errorObject = new stdClass(); $errorObject->apiVersion = '1.0'; $errorObject->context = 'upload.submittal'; # ABOUT ERROR object $aboutError = new stdClass(); $aboutError->code = 'ERR-000'; $aboutError->message = 'Select file to upload.'; # APPEND ABOUT ERROR object TO ERROR object $errorObject->error = $aboutError; # RETURN JSON RESPONSE header('Content-type:application/json;charset=utf-8'); return print(json_encode($errorObject)); } /** * Is file extension allowed? * * */ if (!in_array($upload_file_extension, $allowed_file_extensions)) { # ERROR object $errorObject = new stdClass(); $errorObject->apiVersion = '1.0'; $errorObject->context = 'upload.submittal'; # ABOUT ERROR object $aboutError = new stdClass(); $aboutError->code = 'ERR-000'; $aboutError->message = 'Allowed file formats .pdf'; # APPEND ABOUT ERROR object TO ERROR object $errorObject->error = $aboutError; # RETURN JSON RESPONSE header('Content-type:application/json;charset=utf-8'); return print(json_encode($errorObject)); } /** * Is file bigger than 20MB? * * */ if ($_FILES['files']['size'][0] > 20000000) { # ERROR object $errorObject = new stdClass(); $errorObject->apiVersion = '1.0'; $errorObject->context = 'upload.submittal'; # ABOUT ERROR object $aboutError = new stdClass(); $aboutError->code = 'ERR-000'; $aboutError->message = 'File is too large. File size should be less than 20 megabytes.'; # APPEND ABOUT ERROR object TO ERROR object $errorObject->error = $aboutError; # RETURN JSON RESPONSE header('Content-type:application/json;charset=utf-8'); return print(json_encode($errorObject)); } /** * Does file already exist? * * */ if (file_exists($upload_file)) { /** * File overwritten successfuly! * * */ move_uploaded_file($_FILES['files']['tmp_name'][0], $upload_file); # SUCCESS object $successObject = new stdClass(); $successObject->apiVersion = '1.0'; $successObject->context = 'upload.submittal'; $successObject->status = 'OK'; # UPLOAD SUBMITTAL object $data = new stdClass(); $data->submittalUploaded = true; # APPEND DATA object TO SUCCESS object $successObject->data = $data; # APPEND empty arrays to DATA object $successObject->data->arr1 = []; $successObject->data->arr2 = []; $successObject->data->arr3 = []; # RETURN JSON RESPONSE header('Content-type:application/json;charset=utf-8'); return print(json_encode($successObject)); } /** * Can file actually be uploaded? * * */ if (!move_uploaded_file($_FILES['files']['tmp_name'][0], $upload_file)) { /** * File upload error! * * */ # ERROR object $errorObject = new stdClass(); $errorObject->apiVersion = '1.0'; $errorObject->context = 'upload.submittal'; # ABOUT ERROR object $aboutError = new stdClass(); $aboutError->code = 'ERR-000'; $aboutError->message = 'File couldn\'t be uploaded.'; # APPEND ABOUT ERROR object TO ERROR object $errorObject->error = $aboutError; # RETURN JSON RESPONSE header('Content-type:application/json;charset=utf-8'); return print(json_encode($errorObject)); } else { /** * File uploaded successfuly! * * */ # SUCCESS object $successObject = new stdClass(); $successObject->apiVersion = '1.0'; $successObject->context = 'upload.submittal'; $successObject->status = 'OK'; # UPLOAD SUBMITTAL object $data = new stdClass(); $data->submittalUploaded = true; # APPEND DATA object TO SUCCESS object $successObject->data = $data; # APPEND empty arrays to DATA object $successObject->data->arr1 = []; $successObject->data->arr2 = []; $successObject->data->arr3 = []; # RETURN JSON RESPONSE header('Content-type:application/json;charset=utf-8'); return print(json_encode($successObject)); // We could insert URL file path to a database from here... } } /** * Function methodInvalid() warns about invalid method. * * */ function methodInvalid() { # ERROR object $errorObject = new stdClass(); $errorObject->apiVersion = '1.0'; $errorObject->context = 'uploads'; # ABOUT ERROR object $aboutError = new stdClass(); $aboutError->code = 'ERR-000'; $aboutError->message = 'Invalid Request. Allowed Methods are POST.'; # APPEND ABOUT ERROR object TO ERROR object $errorObject->error = $aboutError; # RETURN JSON RESPONSE header('Content-type:application/json;charset=utf-8'); return print(json_encode($errorObject)); } ?>