Codeskill

Learn to code, step by step

File uploads securely

Avatars, attachments, CSV imports. It is also a common way to get malware on your server if you are careless.

Form setup

<form method="post" enctype="multipart/form-data">
  <input type="hidden" name="csrf" value="...">
  <input type="file" name="avatar" accept="image/jpeg,image/png,image/webp">
  <button type="submit">Upload</button>
</form>

Validate before you move

$file = $_FILES['avatar'] ?? null;

if (!$file || $file['error'] !== UPLOAD_ERR_OK) {
    throw new RuntimeException('Upload failed');
}

$maxBytes = 2 * 1024 * 1024; // 2 MB
if ($file['size'] > $maxBytes) {
    throw new RuntimeException('File too large');
}

$finfo = new finfo(FILEINFO_MIME_TYPE);
$mime = $finfo->file($file['tmp_name']);
$allowed = ['image/jpeg' => 'jpg', 'image/png' => 'png', 'image/webp' => 'webp'];

if (!isset($allowed[$mime])) {
    throw new RuntimeException('Invalid file type');
}

Check MIME type from file contents, not the client-supplied filename. evil.php.jpg is still a problem if you trust the extension alone.

Store outside public or serve safely

Best option: save uploads outside public/ and serve them through a PHP script that checks the user is allowed to view the file. If they must be public (static avatars), use a random filename and never allow .php execution in the upload directory.

$ext = $allowed[$mime];
$name = bin2hex(random_bytes(16)) . '.' . $ext;
$dest = dirname(__DIR__) . '/storage/uploads/' . $name;

if (!move_uploaded_file($file['tmp_name'], $dest)) {
    throw new RuntimeException('Could not save file');
}

// Save $name in the database linked to the user

Web server hardening

  • Disable PHP execution in the upload directory (Apache php_flag engine off)
  • Set a disk quota or max upload size in php.ini
  • Scan or reject double extensions
  • Never use the original filename as the stored path

Uploads are trust boundaries. Treat every file as hostile until you have checked size, type, and destination.

PreviousAuthorisation – roles and can this user do X?