WordPress File Upload Security: Preventing Malware Uploads
Secure file uploads in WordPress by validating file types, scanning for malware, and restricting upload permissions.
File uploads are a common attack vector. Attackers upload PHP shells disguised as images or exploit vulnerable upload handlers to gain server access.
File Upload Attack Vectors
Common Attack Methods
- PHP files disguised as images (shell.php.jpg)
- Double extensions (image.jpg.php)
- Null byte injection (image.php%00.jpg)
- Content-type spoofing
- SVG with embedded JavaScript
- Polyglot files (valid image + valid PHP)
WordPress Upload Validation
Default Allowed Types
// WordPress default allowed mime types
// Images: jpg, jpeg, png, gif, ico, webp
// Documents: pdf, doc, docx, ppt, pptx, xls, xlsx
// Audio: mp3, m4a, ogg, wav
// Video: mp4, m4v, mov, wmv, avi, mpg
Restrict Upload Types
// Limit allowed upload types
add_filter('upload_mimes', 'wpfs_restrict_uploads');
function wpfs_restrict_uploads($mimes) {
// Remove potentially dangerous types
unset($mimes['svg']);
unset($mimes['svgz']);
unset($mimes['exe']);
unset($mimes['htm']);
unset($mimes['html']);
// Only allow specific types
return array(
'jpg|jpeg' => 'image/jpeg',
'png' => 'image/png',
'gif' => 'image/gif',
'pdf' => 'application/pdf',
);
}
Enhanced File Validation
Verify Actual File Type
// Check actual file content, not just extension
add_filter('wp_handle_upload_prefilter', 'wpfs_validate_upload');
function wpfs_validate_upload($file) {
// Check file extension
$ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
// Get actual mime type from file content
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$actual_mime = finfo_file($finfo, $file['tmp_name']);
finfo_close($finfo);
// Verify image files are actually images
$image_mimes = array(
'image/jpeg', 'image/png', 'image/gif', 'image/webp'
);
if (in_array($ext, array('jpg', 'jpeg', 'png', 'gif', 'webp'))) {
if (!in_array($actual_mime, $image_mimes)) {
$file['error'] = 'File type does not match extension.';
}
}
return $file;
}
Scan for PHP in Images
// Scan uploaded files for PHP code
function wpfs_scan_for_php($file_path) {
$content = file_get_contents($file_path);
$dangerous_patterns = array(
'
Upload Directory Security
Disable PHP in Uploads
# .htaccess in wp-content/uploads/
<FilesMatch "\.php$">
Order Allow,Deny
Deny from all
</FilesMatch>
# Alternative - deny all scripts
<FilesMatch "\.(php|phtml|php3|php4|php5|php7|phps|cgi|pl|py)$">
Deny from all
</FilesMatch>
Nginx Configuration
# Disable PHP execution in uploads
location ~* /wp-content/uploads/.*\.php$ {
deny all;
}
# Block access to hidden files
location ~ /\. {
deny all;
}
File Size and Quantity Limits
Limit Upload Sizes
// Limit upload size per file type
add_filter('upload_size_limit', 'wpfs_upload_size_limit');
function wpfs_upload_size_limit($size) {
// 2MB for regular users
if (!current_user_can('manage_options')) {
return 2 * 1024 * 1024;
}
return $size;
}
Rename Uploaded Files
Sanitize File Names
// Rename files to prevent execution tricks
add_filter('sanitize_file_name', 'wpfs_sanitize_filename', 10);
function wpfs_sanitize_filename($filename) {
// Remove multiple extensions
$parts = explode('.', $filename);
if (count($parts) > 2) {
$ext = array_pop($parts);
$filename = sanitize_title(implode('_', $parts)) . '.' . $ext;
}
// Remove special characters
$filename = preg_replace('/[^a-zA-Z0-9._-]/', '', $filename);
return $filename;
}
User Permission Controls
Restrict Upload Capability
// Remove upload capability from certain roles
add_action('admin_init', 'wpfs_restrict_upload_role');
function wpfs_restrict_upload_role() {
$contributor = get_role('contributor');
$contributor->remove_cap('upload_files');
$subscriber = get_role('subscriber');
$subscriber->remove_cap('upload_files');
}
Front-End Upload Security
- Always validate on server side
- Use nonces for upload forms
- Require authentication for uploads
- Implement CAPTCHA for public uploads
- Rate limit upload attempts
Conclusion
Secure file uploads require multiple validation layers: extension checking, mime type verification, content scanning, and execution prevention. Never trust file extensions alone.
Written by Sarah Chen
WP Folder Shield Team