File uploads

Accept a photo or resume without letting an attacker upload a script.

PHP
<?php
$maxBytes = 2 * 1024 * 1024;
$allowed = ["image/jpeg" => "jpg", "image/png" => "png", "application/pdf" => "pdf"];
$error = "";

if ($_SERVER["REQUEST_METHOD"] === "POST") {
    $file = $_FILES["resume"] ?? null;
    if (!$file || $file["error"] !== UPLOAD_ERR_OK) {
        $error = "Upload failed, please try again";
    } elseif ($file["size"] > $maxBytes) {
        $error = "File must be under 2 MB";
    } else {
        $type = (new finfo(FILEINFO_MIME_TYPE))->file($file["tmp_name"]);
        if (!isset($allowed[$type])) {
            $error = "Only JPG, PNG or PDF";
        } else {
            $name = bin2hex(random_bytes(16)) . "." . $allowed[$type];
            move_uploaded_file($file["tmp_name"], __DIR__ . "/../uploads/" . $name);
        }
    }
}
?>
<form method="post" enctype="multipart/form-data">
  <input type="file" name="resume" accept=".jpg,.png,.pdf" required>
  <button>Upload</button>
</form>
<p><?= htmlspecialchars($error) ?></p>
  • Without enctype="multipart/form-data", the file never arrives.
  • Check the real type with finfo. The file name and the type the browser reports can both be faked.
  • Give the file a new random name. Never keep the user's name, which could be ../../index.php.
  • Store uploads outside the public web folder, or stop PHP from running in that folder.
  • upload_max_filesize and post_max_size in php.ini set the largest file the server accepts.
โ† Forms and output safetyPHP and MySQL โ†’