WordPress Security Basics That Stop Most Attacks
Advertisement
When I clean up a compromised WordPress site, the root cause is almost never a highly sophisticated, targeted attack. It is simply an outdated plugin with a widely known vulnerability that was publicly patched months ago. It is a wp-admin login with a weak password that was breached in some completely unrelated leak. Or it is an old theme that has absolutely not been touched since the site launched, carrying a file upload vulnerability nobody has looked at since.
WordPress's sheer popularity means automated scanners are constantly, relentlessly probing the entire internet for exactly these things. The completely unglamorous basics below are what actually closes that door.
Updates: the single biggest factor
The vast majority of WordPress compromises trace directly back to a plugin or theme with a publicly known vulnerability that simply had not been updated. WordPress core handles its own minor security updates automatically by default, but plugins and themes absolutely do not unless explicitly configured to:
// wp-config.php
add_filter('auto_update_plugin', '__return_true');
add_filter('auto_update_theme', '__return_true');
For critical sites where a plugin update breaking something is a genuinely bigger concern than the brutal security risk of not updating, setting up a staging environment that gets updates first (with production following a few days later) is a perfectly reasonable middle ground.
What is absolutely not reasonable is "we will update when we get around to it." Because "when you get around to it" is exactly the window automated scanners are desperately looking for.
Remove what isn't actively being used
Every single inactive plugin and theme is still physical code sitting on the server. And "inactive" absolutely does not mean "not exploitable." A vulnerability in a deactivated plugin's files can very often still be reached directly if the file itself is publicly accessible over the web.
The fix is not deactivating it. The fix is aggressively deleting it:
wp plugin list --status=inactive
wp plugin delete <plugin-name>
If it is actually needed again later, it can be reinstalled in thirty seconds. Sitting completely unused on disk, it is pure, unadulterated attack surface with absolutely zero benefit.
Login security: where most automated attacks violently land
/wp-login.php and /xmlrpc.php are aggressively hit by automated login attempts constantly, completely regardless of how obscure or small the site is. A few basic changes address almost all of this:
Strong, completely unique passwords and 2FA for absolutely every single account with publish or admin access. This does not just mean the main admin account. A forgotten contributor account with a weak password is still a foothold into the system.
Limiting login attempts, either via a solid plugin or, much better, at the server level using a Fail2ban jail actively watching for repeated failed logins against wp-login.php:
# /etc/fail2ban/jail.local
[wordpress]
enabled = true
filter = wordpress
logpath = /var/log/nginx/access.log
maxretry = 5
bantime = 3600
Disabling XML-RPC entirely if absolutely nothing (no custom app, no Jetpack-style integrations) actually uses it. It is a massive, common target for both direct brute-force attempts and aggressive amplification in pingback-based attacks, and the vast majority of sites have absolutely zero legitimate use for it today.
Locking down the admin area
DISALLOW_FILE_EDIT brutally removes the built-in theme and plugin file editor from the WordPress admin dashboard. That is the terrifying editor that lets anyone with admin access edit raw PHP files directly through the web browser. If an admin account is ever compromised, this cuts off one of the easiest ways for that compromise to instantly turn into arbitrary code execution:
// wp-config.php
define('DISALLOW_FILE_EDIT', true);
File permissions matter massively too. WordPress's own strict recommendation of 644 for files and 755 for directories (with wp-config.php aggressively locked down to 600) means that even if a severe vulnerability allows writing to the filesystem, it is heavily constrained by what the web server's Linux user is actually permitted to touch.
A web application firewall catches what's not yet patched
Updates perfectly handle known vulnerabilities once a patch exists. But the gap between a vulnerability being discovered and a patch being released—and the even wider gap between a patch being released and every single site actually applying it—is exactly where a web application firewall tightly earns its keep.
ModSecurity with the OWASP Core Rule Set, or a highly solid plugin-based WAF, aggressively blocks a massive amount of generic exploit patterns (SQL injection attempts, path traversal, common payload signatures) completely regardless of which specific plugin they are desperately targeting. This means it is incredibly useful even against zero-day vulnerabilities that nobody has written a specific rule for yet.
Why these boring basics are still the answer
Absolutely none of this is advanced, and that is exactly the point. The vast, overwhelming majority of WordPress compromises are not exotic zero-days. They are heavily known issues with readily available fixes that simply were not applied, on sites where basic login security was an afterthought.
Aggressive updates, brutally removing unused code, locking down logins, and running a WAF perfectly address the exact categories that automated attacks actually target, in roughly the exact order of how often each one is the actual root cause. Getting these completely right does not magically make a site invulnerable, but it aggressively moves it out of the enormous, low-hanging pool of sites that automated scanners trivially compromise without any real effort.
Advertisement