I am trying to create a secure file upload using PHP 7+ where I only allow PDF files. I found a lot of posts on this topic on different websites but couldn't find a complete solution that ensures that no harmful files can be uploaded this way.
So far I have the following code. Can someone tell me if I am missing any important steps here or if anything should be changed or removed in my code?
(Note: I am not interested in the old x-pdf file types.)
<?php
include 'session.php';
include 'header.php';
if (empty($_FILES['files'])) {
echo json_encode(['error'=>'No files found for upload.']);
return;
}
if(!empty($_POST['csrfToken'])) {
if(hash_equals($_SESSION['csrfToken'], $_POST['csrfToken'])) {
$postData = $_POST;
$files = $_FILES['files'];
$uploadRef = preg_replace('/[^A-Za-z0-9]/', '', $_GET['uploadRef']);
$categoryId = preg_replace('/[^A-Za-z0-9]/', '', $_GET['categoryId']);
$tags = preg_replace('/[^A-Za-z0-9,]/', '', $_GET['tagsList']);
$success = null;
$paths= [];
$filenames = $files['name'];
for($i=0; $i < count($filenames); $i++){
if($_FILES['file']['error'] !== UPLOAD_ERR_OK) {
die('Upload failed with error ' . $_FILES['file']['error']);
}
$fileTitle = $files['name'][$i];
$fileTitle = substr($fileTitle, 0 , (strrpos($fileTitle, ".")));
$fileExtensions = explode('.', basename($filenames[$i]));
$fileExtension = strtolower(array_pop($fileExtensions));
$ok = false;
switch($fileExtension) {
case 'pdf':
$ok = true;
default:
die('Unknown/not permitted file type');
}
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime = finfo_file($finfo, $_FILES['file']['tmp_name']);
$ok = false;
switch($mime) {
case 'application/pdf':
$ok = true;
default:
die('Unknown/not permitted file type');
}
$uploadId = md5(uniqid()) . '_' . $i;
$target = 'uploads' . DIRECTORY_SEPARATOR . $uploadId . '.' . $fileExtension;
if(move_uploaded_file($files['tmp_name'][$i], $target)) {
$success = true;
$paths[] = $target;
$conn = new mysqli($dbHost, $dbUser, $dbPw, $dbName);
if($conn->connect_error) {
exit($trans['errorConnectionFailedTxt'][$lang]);
}
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$conn->set_charset('utf8mb4');
$stmt = $conn->prepare("INSERT INTO uploads (uploadId, uploadRef, categoryId, tags, fileTitle, fileExtension) VALUES (?, ?, ?, ?, ?, ?)");
$stmt->bind_param("ssssss", $uploadId, $uploadRef, $categoryId, $tags, $fileTitle, $fileExtension);
$stmt->execute();
$stmt->close();
$conn->close();
} else {
$success = false;
break;
}
}
if ($success === true) {
$output = [];
} elseif ($success === false) {
$output = ['error'=>'Error while uploading images. Contact the system administrator'];
foreach ($paths as $file) {
unlink($file);
}
} else {
$output = ['error'=>'No files were processed.'];
}
unset($postData);
echo json_encode($output);
} else {
echo json_encode('invalid CSRF token');
}
} else {
echo json_encode('no CSRF token');
}
?>
header('Location...')
calls are you planning to make? \$\endgroup\$