A Boring Security Checklist for WordPress
The most useful security advice for WordPress is also the least exciting. There is no clever trick that makes up for skipping the basics, and no plugin that does the basics for you, whatever its marketing page suggests. What works is a short list of boring controls, applied consistently and checked on a schedule. This post is that list, with the reasoning behind each item so you can adapt it to your own site.
Start with the way people log in
Turn on two-factor authentication for every administrator account, with no exceptions. Yes, even the account you "barely use", and yes, even the one belonging to a contractor. Rarely used accounts tend to have the weakest passwords and the least attention, which makes them exactly where an attacker will look first.
Use a password manager and let it generate every password. Never reuse a password across services. A breach at some unrelated service that one of your administrators signed up to years ago is a realistic way into your site if that person reused the password.
Give every person the smallest role that lets them do their job. Authors and editors do not need administrator rights, and an account that cannot install plugins cannot be used to install a malicious one. Review the user list at least twice a year and remove anyone who no longer works with you.
Finally, decide what to do about XML-RPC. It is an older remote publishing interface that is enabled by default and that many sites never use. If nothing you run depends on it, disabling it removes one more door that automated scanners like to try.
Treat every plugin as a decision
Installing a plugin gives code written by someone you have no relationship with the ability to run on your server and read your database. That is not a reason to avoid plugins; it is a reason to treat each installation as a decision with a cost. Plugins and themes are a frequent entry point for attacks, so a smaller, better-maintained set is almost always safer.
Audit your plugins twice a year. Remove what you do not use. For each one you keep, confirm that the developer is still active, that updates have shipped recently, and that its listing shows it is still maintained. A plugin that was last updated several years ago is a risk even if it still works.
When a security advisory is published for something you run, update quickly. Advisories are public, and so are the details of what they fix, which means attackers can read them just as you can. The gap between a published fix and your update is the window in which your site is exposed, so keep it short. Turning on automatic updates for plugins you trust is one simple way to shorten it.
Keep the platform underneath current as well. PHP versions have a published support calendar, and a version that is no longer on the supported versions list receives no more security fixes. Plan your upgrade before the date arrives, not after.
Lock down the server side
The file wp-config.php holds your database credentials and secret keys, so it should be readable only by the account that runs the web server. No other process or user on the machine needs to see it.
Do not run debug mode in production. Setting WP_DEBUG to true can expose internal file paths, query details and stack traces to anyone who can trigger an error. If you need a log on a live site, write it to a file outside the web root instead of printing errors into pages.
Set strict file permissions. The WordPress hardening guide recommends directories at 755 and files at 644, and warns against making files world-writable. Overly permissive upload and file settings are a classic way for a small flaw to become a full compromise, so never reach for 777 to make an error go away. Also consider disabling the built-in theme and plugin file editor, which the same guide describes, so that a stolen administrator session cannot be used to edit code from the dashboard.
Serve the whole site over HTTPS, including staging subdomains, and keep the certificate renewing automatically. Mixed or missing encryption on a login page undoes much of what the other controls achieve.
Make backups you can actually restore
A backup is only useful if you can restore from it. Test a restoration once a quarter, on a staging copy if you can. The first time will take longer than you expect, and the fourth will be routine. Without a practiced restore, a backup strategy is wishful thinking.
Keep at least one copy off the server. If your host is compromised, or the account is suspended, a backup that lives on the same machine disappears with it. Encrypt backups at rest too: the customer details in your database are the same liability in a leaked backup as they would be in a leaked database.
Watch for the signs of trouble
Monitoring does not need to be sophisticated to be useful. Three habits catch most problems early:
- File changes. New PHP files appearing in
wp-content/uploads/are almost always a sign of compromise, because that folder should hold media, not code. A file integrity check or a security plugin that reports changed files makes this automatic. - New administrators. An administrator account you did not create is a serious warning. A simple daily query on the database does the job. On a standard MySQL setup it looks like
SELECT user_login, user_registered FROM wp_users WHERE user_registered > NOW() - INTERVAL 1 DAY;, with your own table prefix in place ofwp_. - Login noise. Automated attempts against
wp-login.phpare routine. The question is not whether they happen but whether any succeed, so watch your access logs for sudden spikes from a single address and for successful logins from places you do not recognize.
Know your plan before something goes wrong
Decide now what you will do on the day your site is compromised, because on that day you will be under pressure and short of time. A sensible order is:
- Contain it. Take the site offline or put up a maintenance page. Do not try to clean up while visitors and attackers are still hitting the site.
- Preserve evidence. Copy the filesystem, the database and the access and error logs before you change anything. The investigation will need all of it.
- Restore from a clean backup. "Clean" means older than the compromise. If you do not know when it began, go back to a backup older than the oldest suspicious file.
- Rotate every credential. Database password, all user passwords, any API keys in
wp-config.phpand any host-level keys. Assume the attacker read everything they could reach. - Close the entry point and report. Find how they got in and fix it, or you will be back in the same place. If you handle personal data of people in the EU, you may have to notify your supervisory authority within 72 hours of becoming aware of a breach, as Article 33 of the GDPR describes. Ask a qualified advisor what applies to you, and do not delay.
What this list gets you
This list is not comprehensive. It covers the controls that block the most common routes into a WordPress site for the least operational effort. There are more advanced measures, such as web application firewalls, runtime protection and behavioral anomaly detection, and they can be worth having. None of them replaces the boring checklist.
If you can run through the checklist every quarter without flinching, you are already in better shape than many WordPress sites. That is a low bar, but it is the one that tends to decide whether your site is the subject of next month's incident report or not.
Photo: Tara Winstead on Pexels