Cross-site Scripting via File Upload
Description
This vulnerability allows attackers to upload files containing malicious HTML and JavaScript code that executes in victims' browsers. The application accepts file uploads without properly validating content or restricting file types, and serves uploaded files in a way that allows embedded scripts to execute. When users access the uploaded file, the malicious code runs in their browser context, enabling Cross-Site Scripting (XSS) attacks.
Remediation
Implement multiple layers of defense to prevent malicious file uploads and XSS execution:
1. Restrict Allowed File Types:
• Use a strict whitelist of permitted file extensions (e.g., .jpg, .png, .pdf only).
• Validate both the file extension and MIME type on the server side.
• Reject files with double extensions (e.g., file.php.png) or no extension (e.g., .htaccess, web.config).
2. Validate File Content:
• Verify that file content matches the declared file type using magic number validation.
• For images, re-encode them server-side to strip embedded scripts and malicious EXIF data.
• Scan uploaded files for malicious content before storage.
3. Secure File Storage and Serving:
• Store uploaded files outside the web root directory whenever possible.
• Rename uploaded files to random, system-generated names to prevent direct execution.
• Set restrictive file permissions to prevent execution (remove execute permissions).
• Serve uploaded files with appropriate Content-Type headers and use Content-Disposition: attachment to force downloads.
• Implement the X-Content-Type-Options: nosniff header to prevent MIME-type sniffing.
4. Example Secure Implementation (PHP):
// Whitelist allowed extensions
$allowedExtensions = ['jpg', 'jpeg', 'png', 'pdf'];
$fileExtension = strtolower(pathinfo($_FILES['upload']['name'], PATHINFO_EXTENSION));
if (!in_array($fileExtension, $allowedExtensions)) {
die('Invalid file type');
}
// Validate MIME type
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mimeType = finfo_file($finfo, $_FILES['upload']['tmp_name']);
finfo_close($finfo);
$allowedMimes = ['image/jpeg', 'image/png', 'application/pdf'];
if (!in_array($mimeType, $allowedMimes)) {
die('Invalid file content');
}
// Generate random filename and store outside web root
$newFilename = bin2hex(random_bytes(16)) . '.' . $fileExtension;
$uploadPath = '/var/uploads/' . $newFilename; // Outside web root
move_uploaded_file($_FILES['upload']['tmp_name'], $uploadPath);
// When serving files, use proper headers
header('Content-Type: ' . $mimeType);
header('Content-Disposition: attachment; filename="download.' . $fileExtension . '"');
header('X-Content-Type-Options: nosniff');
readfile($uploadPath);
5. Additional Security Measures:
• Implement file size limits to prevent denial-of-service attacks.
• Use a Content Security Policy (CSP) to restrict script execution.
• Consider using a dedicated file storage service or CDN with built-in security controls.
• Regularly audit and monitor uploaded files for suspicious content.