Secure File Upload Handling in WordPress
File uploads are a common attack vector for WordPress sites. Learn how to configure secure file upload handling to prevent malware infections.
Understanding File Upload Risks
File uploads enable users to submit content to your site, but they also create opportunities for attackers to upload malicious files. A single malicious PHP file in your uploads directory can give attackers complete control of your server.
Common Upload Attack Vectors
Executable File Uploads
Attackers attempt to upload:
- PHP webshells
- Malicious JavaScript files
- Executable scripts
- Server-side includes
Extension Spoofing
Malicious files disguised as safe types:
- image.jpg.php
- document.pdf.exe
- file.php.jpg (with null byte)
Content Type Manipulation
Attackers modify MIME type headers to bypass validation that only checks content type.
WordPress Default Protections
Allowed File Types
WordPress restricts uploads to specific extensions by default. The allowed types are defined in wp-includes/functions.php.
MIME Type Checking
WordPress validates file content matches the claimed type using finfo or mime_content_type.
Sanitization
Filenames are sanitized to remove dangerous characters.
Strengthening Upload Security
Restrict Allowed File Types
Limit uploads to necessary types only:
add_filter('upload_mimes', function($mimes) {
// Only allow images and PDFs
return array(
'jpg|jpeg|jpe' => 'image/jpeg',
'gif' => 'image/gif',
'png' => 'image/png',
'pdf' => 'application/pdf'
);
});
Additional MIME Validation
add_filter('wp_check_filetype_and_ext', function($data, $file, $filename, $mimes) {
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$real_mime = finfo_file($finfo, $file);
finfo_close($finfo);
// Verify MIME matches extension
if (!in_array($real_mime, $mimes)) {
return array('ext' => false, 'type' => false, 'proper_filename' => false);
}
return $data;
}, 10, 4);
Protecting the Uploads Directory
Disable PHP Execution
Create .htaccess in wp-content/uploads:
<Files *.php>
deny from all
</Files>
# Block all scripts
<FilesMatch "\.(php|phtml|php3|php4|php5|pl|py|jsp|asp|htm|shtml|sh|cgi)$">
deny from all
</FilesMatch>
Nginx Configuration
location ~* /wp-content/uploads/.*\.php$ {
deny all;
}
Block Directory Browsing
Options -Indexes
File Validation Best Practices
Check File Size
Set reasonable limits in php.ini:
upload_max_filesize = 10M
post_max_size = 10M
Validate Image Files
For images, verify they are valid:
add_filter('wp_handle_upload_prefilter', function($file) {
$image_types = array('image/jpeg', 'image/png', 'image/gif');
if (in_array($file['type'], $image_types)) {
$image = getimagesize($file['tmp_name']);
if ($image === false) {
$file['error'] = 'Invalid image file.';
}
}
return $file;
});
Scan Uploaded Files
Integrate with antivirus scanning:
- ClamAV integration
- Security plugin scanning
- External API scanning services
Handling User Uploads
Separate Upload Locations
Store user uploads outside web root when possible:
// Serve through PHP script that validates access
readfile('/secure/path/outside/webroot/' . $filename);
Rename Uploaded Files
Generate random filenames to prevent path guessing:
$new_filename = bin2hex(random_bytes(16)) . '.' . $extension;
Content Security Headers
Set headers when serving uploaded files:
header('Content-Disposition: attachment');
header('X-Content-Type-Options: nosniff');
Monitoring Uploads
Log Upload Activity
Track all file uploads:
- Uploader username/IP
- Filename and type
- Timestamp
- File location
Periodic Scanning
Regularly scan uploads directory for:
- PHP files (should not exist)
- Recently modified files
- Files with suspicious names
- Known malware signatures
Conclusion
Secure file upload handling is essential for WordPress security. Restrict allowed file types, validate uploads thoroughly, disable PHP execution in uploads, and monitor for suspicious files. These measures prevent attackers from using file uploads to compromise your site.
Written by Sarah Chen
WP Folder Shield Team