Handling File Uploads in PHP Securely

File upload handling is one of the most common places PHP applications get compromised — accepting an “image” that’s actually a PHP script is a classic path to remote code execution.

$allowed = ['image/jpeg', 'image/png', 'image/webp'];
$file = $_FILES['avatar'];

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

// Never trust the client-sent MIME type - check the actual file
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime = finfo_file($finfo, $file['tmp_name']);

if (!in_array($mime, $allowed, true)) {
    throw new RuntimeException('Invalid file type');
}

if ($file['size'] > 5 * 1024 * 1024) {
    throw new RuntimeException('File too large');
}

$newName = bin2hex(random_bytes(16)) . '.jpg';
move_uploaded_file($file['tmp_name'], "/var/uploads/" . $newName);

Store uploads outside the web root when possible, always generate a new random filename rather than trusting the original, and verify the actual file content type — never the client-supplied MIME type or extension alone.