WordPress File Upload Security: Why Blocklists Fail and Allowlists Win

Unauthenticated file upload has been the single most common vulnerability pattern across the CVEs we’ve covered this year — Forminator, GiveWP, Wholesale Lead Capture, File Manager, and more, each a variation on the same theme. The underlying defensive principle that actually prevents this entire category is simpler than any individual patch: validate uploads against an explicit allowlist of permitted file types, never a blocklist of forbidden ones.

Why Blocklists Specifically Fail

A blocklist approach tries to enumerate every dangerous extension and reject those specifically — which sounds reasonable until you consider that some server configurations still execute .phtml, .php5, .php7, .pht, and .phar as PHP, alongside the obvious .php. A blocklist built around blocking just .php misses every one of those variants, and attackers specifically test for exactly this gap. An allowlist structurally can’t make this mistake: if an extension isn’t explicitly permitted, it’s rejected, full stop — there’s no enumeration to be incomplete.

WordPress Core’s Own Validation, and Where It Still Falls Short

Since WordPress 4.7.1, core validates uploaded files against their actual content, not just the claimed extension — checking the extension first, then using PHP’s finfo_file() to verify the real MIME type actually matches. This two-layer check is genuinely effective against the simplest attack (renaming a .php file to .jpg), but it’s a baseline, not a complete solution: it’s exactly the plugin-level upload handlers — custom form builders, donation processors, wholesale registration forms — that repeatedly reintroduce this exact vulnerability class by implementing their own upload logic that bypasses or weakens core’s validation.

What Actually Works

Approach Why
Explicit allowlist of permitted extensions Structurally can’t miss an obscure dangerous extension the way an incomplete blocklist can
Validate actual file content, not just extension/MIME header Defeats MIME type spoofing, where an attacker renames and relabels a malicious file
Use the upload_mimes filter to scope exactly what a specific upload feature needs A photo upload field should never accept executable types at all — scope permissions to the actual use case
Store uploads outside the web root, or block execution in the upload directory Even if a malicious file somehow gets uploaded, it can’t be directly executed via a web request

What to Actually Check on Your Own Site

  1. Audit any plugin with a custom file upload feature (forms, donations, wholesale registration, reviews with photo/video attachments) specifically for whether it relies on its own validation logic or properly defers to WordPress core’s checks.
  2. Confirm your uploads directory doesn’t execute PHP, via server configuration (a .htaccess rule or equivalent on Nginx) as defense-in-depth, independent of any single plugin’s validation quality.
  3. Use the upload_mimes filter to scope what specific upload fields actually need, rather than leaving broad file-type acceptance as a default.
  4. Treat any plugin CVE in this category (file upload RCE) as a pattern to watch for across your entire plugin list, not just the specific plugin named in a given advisory — the same coding mistake shows up repeatedly across unrelated plugins.

This connects directly to several CVEs we’ve covered this year — see CVE-2026-15748: Forminator Forms RCE and CVE-2026-19708: File Manager Plugin for real examples of this exact pattern. Want your site’s upload handling audited? Get in touch.

Frequently Asked Questions

Does WordPress core’s built-in validation make plugin-level checks unnecessary?

No — core’s validation is a genuine baseline, but plugins implementing their own custom upload handlers (rather than relying entirely on core’s path) can still introduce vulnerabilities that bypass or weaken that baseline, which is exactly the pattern behind most of the file-upload CVEs covered this year.

Is blocking the uploads directory from executing PHP enough on its own?

It’s a strong defense-in-depth layer, but not a complete substitute for proper upload validation — a well-configured allowlist plus execution-blocking in the uploads directory together are considerably stronger than either alone.

How do I know if a plugin’s upload feature uses an allowlist or blocklist approach?

This typically requires checking the plugin’s own code or documentation, or relying on a security audit — it’s not usually visible from the admin interface, which is part of why this vulnerability class recurs so often without site owners noticing until a CVE is disclosed.

Featured image: original illustration.


Ready to start your project?

Share your brief and we’ll propose the right approach — a full site, a landing page, or a custom plugin.