Learn all you need to know about WordPress security and how to effectively protect your WordPress websites from hacking attacks.
Welcome to the WordPress Security Handbook 2021. I’ll try to teach you how to effectively protect your WordPress websites from malicious actors and hackers. Knowledge about WordPress security, and knowing how to protect yourself and your clients from malicious actors, makes your job a lot more peaceful.
This handbook is not just a practical guide. I also try to give you an overview of everything important when dealing with WordPress security. First, I’ll walk you through the most common types of attacks you have to protect your WordPress websites from. Then we’ll go through hosting, backups, updates, HTTPS, and other topics that will make you far less vulnerable to such attacks.
If you have found anything missing in this WordPress security handbook, please leave it in the comments below the post. Now let’s get started.
Introduction to the WordPress security handbook
WordPress is the most used CMS in the world. By July 2021 about 42 percent of all websites on the Internet use WordPress as their foundation. The popularity of WordPress makes it an attractive target for black-hat hackers. If you’d find a vulnerability in a rather recent WordPress core version, you could potentially target 10s or even 100s of millions of websites. Cash is to be made. The internet live stats for Websites hacked in real-time illustrate the scale.
However, the WordPress core itself is not that much of a concern for us most of the time. It’s what’s below WordPress and what builds on top of it. If the underlying web server is vulnerable, the WordPress website on top could be in danger too. The same is true for vulnerable plugins and themes and how it’s configured.
There is no way to be completely safe from cyber-attacks. This is true for every single piece of software. However, some standards and conventions help you to protect your WordPress website from most attacks. That’s what I’m going to show you in this handbook.
Common types of attacks / Learning about attacks
Let me show you what we try to protect our websites against before we learn how to safeguard your WordPress websites. With this knowledge, you’ll have a far better idea of what procedures to take when it comes to security issues. So don’t skip over this part!
This is not a complete list of all possible types of hacking attacks, but the most widespread ones performed, that you as a WordPress developer should know.
XSS attacks
If attackers can inject malicious scripts into a website, they can perform so-called Cross-Site Scripting (XSS) attacks. Attackers can then steal session information or perform requests on behalf of the user.
What are scripts in a website?
When we talk about scripts in a website, we’re typically referring to JavaScript in the HTML document. With JavaScript, you can run logic in the client’s browser. For example, the WordPress Block Editor is primarily written in JavaScript.
XSS attacks can be especially critical if the target has admin rights on the website. The attacker would then be able to take control of the entire website in a number of ways.
XSS attacks are prevented by sanitization and escaping.
Sanitization is the process of removing <> and dangerous stuff from scripts or HTML from user input before it is entered into a database.
This is how JavaScript would look like before sanitization:
<script>
alert("Hello world");
</script>
It becomes this after sanitization:
<script>
alert("Hello world");
</script>
Now, it can be safely injected into a website without any browser ever running it.
As a second measure, however, the data should also be escaped before injecting it into HTML. The explanation from the WordPress theme handbook nails escaping the best:
"Escaping is the process of securing output by stripping out unwanted data, like malformed HTML or script tags, preventing this data from being seen as code."
You find a more detailed and general explanation of XSS on the OWASP website.
CSRF attacks
Whenever you make an HTTP request to a server, something is performed on it. For example, by visiting this blog post, you instructed the server to get the post from the database and assemble it into a valid HTML document (let’s forget about cache for a minute). Such requests are so-called non-destructive requests. They don’t change anything.
But there are also con-/destructive HTTP requests you can make that can create, change, or delete data. For example, if you create a blog post in the WordPress backend and click save, you send an HTTP request to the server to save the post to the database. Or if you click the “Delete” button, you send an HTTP request to delete it from the database.
Now, usually, such endpoints are protected from unauthenticated and unauthorized requests. You have to be signed in for example to perform them.
Let’s say you are an admin for a blog, and you have the right to delete blog posts. What if I sent you a link that, if you clicked it, would submit such a delete request to your website?
That would be considered a very simplified version of a so-called Cross-Site Request Forgery (CSRF) attack. Attackers manipulate their victims into doing something that they want them to do.
Nonces help you to protect against CSRF attacks. These are random keys that get generated and appended to links or forms that perform an action. They only work once, and then a new one is generated. If the nonce is not present or invalid, the request gets rejected.
Let’s take the Cache purge button in the top bar in my WordPress backend as an example.
This button links to the following URL:
https://omniploy.com/wp-admin/index.php
?nginx_helper_action=purge
&nginx_helper_urls=all
&_wpnonce=4b4sh4x6bd
I have placed the parameters on separate lines to make them easier to read. Usually, this is all in one line.
This URL includes the nonce at the end. That means I can only perform the request with this link once since the nonce is only valid once.
If somebody would try to trick me into performing a cache purge, they would need to know a valid nonce, otherwise, the request would get rejected, even if I’m signed in.
The same is true for forms. The nonce is there usually injected as a hidden field. An example from the profile edit form in the WordPress backend:
<! -- ... -->
<form id="your-profile" action="https://omniploy.com/wpadmin/profile.php" method="post" novalidate="novalidate">
<input type="hidden" id="_wpnonce" name="_wpnonce" value="391e11d52e">
<! -- ... -->
If you ever find an endpoint that performs some destructive action immediately on request and doesn’t require a nonce, report it to the developers. Otherwise, you risk being duped into unintentionally performing an action you don’t want.
You find a more detailed explanation of CSRF attacks on Wikipedia.
SQL injection
The WordPress installation uses SQL as the language to communicate with your database. For every change on a post, page, etc. a SQL query is performed. Sometimes to read something from the database and sometimes to write something to it. Now, it would be very bad if somebody unauthorized would be able to perform such an SQL query uncontrolled.
If an attacker would be able to inject unvalidated SQL, it’s called an SQL injection attack.
A pretty common SQL query looks like this:
INSERT INTO users (firstname, lastname, email, ...)
VALUES ("John", "Doe", "john@doe.com", ...);
Inserting user input without sanitizing it correctly could end badly.
Now what would happen if somebody would enter the following value into the first name field:
"); DELETE FROM users; --
This example would lead to a complete wipe of the “users” table if the value wouldn’t be sanitized:
INSERT INTO users (firstname, lastname, email, ...)
VALUES (""); DELETE FROM users; -- ", "Doe", "john@doe.com", ...);
As a result, user input is sanitized from SQL queries before the query is run, the same as it is for dangerous JavaScript. That way you prevent SQL injection attacks.
More on SQL injection on the W3Schools website.
Brute force attacks
In a brute force attack, a malicious actor tries to guess a password by trying many candidates for a particular account. Attackers often use password databases with common passwords, hence also called dictionary attacks.
Without any limits on login tries (or Captchas on the login form), an attacker could test potentially thousands of passwords in a matter of seconds.
That’s why it’s important to use strong passwords, that don’t relate to you in any way. No birthdays or child names in the password. Only random characters or words. Attackers could easily run personalized dictionary attacks.
You should also check if your passwords are in some sort of password database. This can be checked in the Pwned Passwords database by Have I Been Pwned, for example. Many password managers also support checks for this out-of-the-box.
More on brute-force attacks on Wikipedia.
Man-in-the-middle (MITM) attacks
Man-in-the-middle attacks, or also called monkey-in-the-middle attacks, are attacks in which the attacker places themselves between client and server. This can be done in multiple ways. For instance, through a switch, gateway, or any other node between the server and the client.
MITM-Attacks are the reason, why we use encrypted connections nowadays. HTTPS ensures end-to-end encryption to prevent any actor in between from reading along.
More on Man-in-the-middle attacks on Wikipedia.
Denial-of-service (DoS) attacks
Denial-of-service attacks have the goal to bring your WordPress website down to deny access to other visitors. DoS attacks are performed by overwhelming your server with requests until it can’t handle any other requests anymore.
Cloudflare has a great post on Slowloris DoS attacks which explains the concept pretty well.
Distributed denial-of-service (DDos) attacks
DDoS attacks are like normal DoS attacks, but with distributed computing power. Instead of one computer trying to bring down the server, a collection of computers are. The number of controlled nodes can vary from a few to 1000s or 100.000s.
Big companies are regularly confronted with big DDoS campaigns against them.
Social engineering
Social engineering means manipulating somebody or a whole group into doing something the attacker wants them to do. Social engineering is often practiced in combination with XSS or CSRF attacks.
Consider a phishing email from PayPal that asks you to confirm your password or anything similar. They want you to go to some fake login page and enter your credentials. If you enter your credentials, you have been socially engineered. The hackers fish for login credentials, hence also called phishing attacks.
Sophisticated social engineering campaigns are typically directed towards influential people. Administrators or highly-privileged users. The more power you have (for example managing websites) the more you have to watch out for getting tricked.
More on social engineering in this post by imperva.
Supply chain attacks
Another sometimes practiced type of attack in the WordPress environment is taking over the update infrastructure for a plugin, for example. Also called a supply chain attack. This can be done by buying a plugin from another developer or by hacking the developer’s account.
That’s why it’s so important to choose plugins from reputable developers. When a plugin update is installed it basically replaces the old plugin source files with the new ones. Meaning, a plugin update can contain any source code the developer wants to put in, including malicious source code.
The recent SolarWinds attacks were also supply-chain attacks.
More on supply chain attacks on Wikipedia.
Hacking outdated software
Hacking outdated software is probably the most common type of WordPress security vulnerability. Outdated plugins, themes, or even underlying server software contain publicly known security vulnerabilities that have been patched in newer versions. If the software (plugins, themes, server software) is not updated regularly, these vulnerabilities don’t get patched. Hence, making you vulnerable.
More on updates later in this guide.
Zero-day vulnerability attacks
Zero-day vulnerabilities are flaws that have yet to be discovered by the developer or the public. A zero-day attack occurs when a malicious actor discovers a vulnerability and exploits it rather than reporting it to the developer.
Zero-day attacks are not that common on smaller WordPress websites. They usually target the “big fishes”. That’s because finding zero-day vulnerabilities is hard and cost-intensive. It’s in the interest of the attacker to keep a zero-day vulnerability as long as possible unknown to the public. But the more a vulnerability is exploited, the higher the chance of getting detected and patched.
More on zero-day vulnerabilities on Wikipedia.
Hosting providers in the WordPress security context
Hosting providers are one of the most critical parts when talking about WordPress security. You can invest as much time and money as you want in making WordPress and all the plugins as secure as possible, if the underlying infrastructure is compromised.
Types of hosting
There are different types of hosting you can choose from. Which one is best for you, depends on your budget and how much time you’d like to invest in maintenance.
Managed WordPress hosting
A managed WordPress hosting provider is the most expensive option, but also with the most dedicated WordPress team. They also usually provide WordPress-specific tools and systems like extended Auto-Update processes, WAFs (Web Application Firewalls – later more on that), staging environments, and much more.
Examples:
General managed hosting
These are hosting providers which don’t exclusively focus on WordPress. They typically provide a management interface like cPanel and let you install a lot of different types of systems. If you choose this type of hosting, you should pay very close attention to security. Many of these hosting providers are slow with server updates, and we don’t want any already-patched vulnerabilities unpatched on our server.
Examples:
Dedicated / VPS hosting
A dedicated physical or virtual private server (VPS) is a physical (or virtual) server somewhere in a data center that you can rent and do whatever you want. You have to install an operating system, the software required to run a web server, and you have to maintain it in the future.
This is the most low-level option when setting up hosting, besides buying physical servers and building the infrastructure on-site.
There are also managed VPS solutions. This is like shared hosting, but without sharing your server with others. The hosting provider is taking care of the underlying operating system and the software required on a web server.
Examples:
Cloud hosting
Cloud hosting is like dedicated hosting but with the difference, that you only pay for the resources that you use. You can easily scale up on peak times and scale down when the resources aren’t needed. However, this comes at a premium. Cloud resources are, compared to dedicated servers or VPS relatively expensive.
Thanks to virtual infrastructure, the differences between dedicated hosting and cloud hosting are shrinking. Many hosting providers provide both, and you can sometimes switch between them with one click or only a few steps.
Examples:
Uptime
Uptime should be at least 99 percent. Website downtime is bad for SEO, user experience, and revenue.
SEO is especially critical for websites that get most of their traffic from Google. Google uses downtime as a ranking factor. They rely on you to be there when they send you traffic. Being down multiple times a year can damage your search engine ranking in the long term.
For e-commerce stores, this could be even more damaging. Every minute a store is down, no orders can be placed.
Check that your hosting provider is transparent with downtimes. They usually have a status page with the uptime. There you can check what has been down when.
Check out the WPMU DEV status page as an example.
Up-to-date software
Your hosting provider (or you if you host yourself) should regularly install software updates and replace old, outdated software that doesn’t get updates anymore. PHP, MySQL or MariaDB and your operating system. Everything needs updates regularly.
Security protocols
Your hosting provider should have solid prevention protocols to prevent attacks in the first place, but also an incident response plan. You have to know everything when a data breach happens and also inform your users. You are obligated to do that, so don’t skip that if you are ever confronted with that!
Backups in the WordPress security context
Ever had a situation in which you accidentally deleted or lost data? Can you imagine what happens if you lose a live client website? What would you do?
In the context of WordPress security, we make backups for the worst case. For example, if data gets encrypted by an attacker or if everything gets deleted.
Losing data is one of the worst things that can happen to you. It’s damaging for you, your client, and it’s also quite unprofessional. The first plugin you should install in any new WordPress website is a backup plugin if you don’t have any strategy on the hosting side for backups.
You’ll probably want to use an automated plugin or a hosting backup solution if you want to make backups on a regular basis. Most hosting providers offer some sort of backup tool for your WordPress websites. If not, you can choose a plugin.
Depending on how often you change something on a website, the interval for the backups can vary. If you publish a lot on your site, I’d recommend a daily database backup and a file backup every week (except if you work a lot with media. Then you can also go with daily backup for files).
Onsite backups
On-site backups are those that are kept on-site, such as on the web server or on storage that can be accessed remotely.
Onsite backups are quick for recovery since the backup is directly accessible. Onsite backups should be created in more regular intervals than offsite backups.
Even if you create onsite backups, you should still create offsite backups.
Offsite backups
The backed-up system does not have direct access to offsite backups. They are stored off-site and backups cannot directly be restored. Before you can restore, you must actively obtain the backup from somewhere (for example, an external hard drive) and put it on the system you wish to restore to.
If you already have onsite backups, you should still create offsite backups at regular intervals. That way, you always have backups available even if the worst-case scenario arises and onsite backups are compromised.
Backup plugins
There are many backup plugins you can use to backup your WordPress website.
Here is a list with popular ones:
They all do pretty much the same. Backing up files and database. You can configure them based on your needs.
However, keep in mind that if your site gets too big (in terms of space used) the backup process can drastically reduce your website speed and take a long time to back up. If that’s the case, I’d recommend going with a solid solution on the hosting site.
Hosting backups
Most managed WordPress hosting services provide some sort of hosting-side backup solution. Depending on the provider, you can configure backups for the complete website, database, files, or even the complete server.
Hosting providers that don’t specialize in WordPress, generally also have backups options.
Here a few hosting providers with great backup solutions:
Testing integrity of backups
You should also do regular restores in a test environment to verify the integrity of your backups, every few months or so.
Even better, integrate backups into your development cycle. Don’t move files and databases manually when you move from dev to live or to staging. Use backups instead. This way you always know that backups are consistent.
Updates in the WordPress security context
Keeping your WordPress websites up to date is one of the most critical things you can do to avoid having your site hacked.
Software is never perfect. Reputable developers try their best to keep the software as free from vulnerabilities as possible. However, sometimes there are dangerous vulnerabilities found that could harm your website. Developers then release updates that close (patch) these holes.
That’s why is so important to regularly update the software on your system. Having a process in place for updating your websites is key to security.
WAFs (we’ll talk about them later) can protect you from getting exploited by many of these vulnerabilities. However, it’s not a replacement for updating your systems. Updates also often include bug fixes or bring new functionality. So there are many reasons to keep your WordPress website updated.
You have to be sure to update your systems from top to bottom. Besides your WordPress core installation, plugins, and themes, other parts need updates as well like PHP, the web server engine, or the operating system itself.
WordPress gives you the option for Automatic Updates. However, this option should be used with caution. You should only do this on small websites with very few plugins in place that have a good track record on non-destructive updates.
Unfortunately, you cannot enable auto-update for security updates only, since there is no differentiation between security updates and functional updates. Minor version changes can still introduce breaking changes (even if unusual). This means you have to treat any update the same. So keep the number of plugins and themes on your WordPress websites low.
Let’s explore them further.
WordPress core updates
WordPress’s exposure makes it probably one of the most secure pieces of software in the world. Many security vulnerabilities are patched, which possibly wouldn’t be found or at a much later time in other less popular systems. More eyes see more.
Also, big companies like Automattic (the parent company of WordPress.com), WPEngine, and many more invest heavily in the WordPress core, to keep it as secure as possible. It’s in their best interest since they use it as their foundation themselves. WordPress.com is simply a WordPress multisite installation with many customizations and a lot of resources.
This means that the WordPress core is not that much of a concern for us, security-wise. However, installing WordPress core updates is still important and best practice. WordPress evolves all the time and at some point, plugin and theme developers drop support for older versions. This means if you don’t update the WordPress core, at some point you also can’t update plugins, themes, and even PHP versions anymore. And updating at that point will be no fun!
You can find a list with all identified WordPress core vulnerabilities in the WPScan vulnerability database (prev. WordPress vulnerability database – wpvulndb.com).
Plugin and theme updates
Keeping plugins and themes updated is important. Most plugins and themes are not developed with the same awareness for security as WordPress core. Thus, they are creating a perfect attack surface.
There are regularly severe security vulnerabilities in plugins and themes detected, and you better update regularly if you don’t want to be vulnerable to them after they have been made public.
Check out the section Reputation of the developer to learn more about what to check for before installing a plugin or theme.
System updates
If you choose a good managed hosting solution, this is not that important for you. However, if you choose to manage to host on your own, you also have to manage updates for software in your setup. This includes PHP, the webserver (e.g. Apache), the operating system, and every other piece of software on that server.
That’s one reason, why I’d recommend you to go with managed hosting solution unless you actively want to manage the underlying system.
HTTPS
You should always assume that you are being watched if you visit a website that uses unencrypted HTTP. Never enter login credentials into unencrypted websites. If you accidentally do, change your password over an HTTPS connection.
Besides HTTP being insecure, it also impacts your SEO. Since 2014 Google uses HTTPS as a ranking factor. I’d consider this a no-brainer because it’s so simple to do nowadays and you’re much safer.
Swithcing WordPress to HTTPS
Switching to HTTPS is relatively easy nowadays. There is simply no reason to communicate over HTTP any more thanks to Let’s Encrypt. Do yourself and your visitors a favor and switch to HTTPS if you haven’t already.
Getting a TLS certificate
Long gone are the days when you had to pay hundreds of dollars to buy a certificate and install it through the command line, only to renew it every other year and start this process all over again. Let’s Encrypt lets you generate domain verified TLS certificates. With their client software, they even automate the whole process. It requests the certificate, installs it on the server, and renews it automatically. Many hosting providers even support this with one click now. Completely for free.
What's the difference between TLS and SSL?
SSL (Secure Sockets Layer) is the old name for the encryption protocol. TLS was first used in 1999 with TLS version 1.0. RIP SSL
Absolute vs. relative URLs
Absolute URLs include the domain. Relative URLs, on the other hand, do not, making them context-dependent.
Example absolute and relative URLs:
- Absolute URL: https://omniploy.com/about/
- Relative URL: /about/
The browser will automatically refer to the domain the link is on when using relative URLs. So if the domain would be example.com instead of omniploy.com, the relative URL also would be https://example.com/about/.
WordPress and many plugins often use absolute URLs in the database and therefore if we want to switch to HTTPS, we have to do a search and replace in the database for the protocol and domain in the database.
Search and replace http-URLs with https-URLs
Depending on the size of your WordPress website, the process of searching and replacing URLs can take a long time.
Please keep in mind, that you directly manipulate the database. You should always create a full backup before doing a search and replace in the database.
Most of the time I use the Better Search Replace plugin to do search-replace, however, every search and replace plugin is usually sufficient. It’s just important that the system you use does support serialization.
WordPress has a way to save objects in the database in a serialized way. Alongside that, the length of the data is saved. Meaning if your URL first has 10 characters and suddenly 11, it has to be changed to otherwise the data is invalidated.
This is also the reason why I highly recommend not to do a search and replace on a SQL file. You could theoretically do an easy search and replace in an editor, however, the serialized data is not considered there, leaving you with inconsistent data.
Here is an example of the strings you should search and replace to switch your WordPress website from HTTP to HTTPS:
- Search for: http://yourdomain.com
- Replace with: https://yourdomain.com
Better search and replace screenshot for omniploy.com
If you mixed www and non-www versions you should also replace them too. If you want, you can also bring more consistency into the links by removing the www (or adding it, depending on your preferences) from URLs.
- Search for: http://www.yourdomain.com
- Replace with: https://yourdomain.com or https://www.yourdomain.com
WordPress 5.7+: One-click switch
WordPress 5.7 has introduced a one-click feature to switch from HTTP to HTTPS. However, this will not change the replaced URLs in the database. It just switches the site URL and home URL to HTTPS and replaces HTTP URLs in the back- and frontend on the fly. That’s OK while migrating to HTTPS, but the database should be updated nonetheless.
This feature is more of a hook extension, which allows managed hosting providers or plugin developers to hook into these processes and do the migration specifically. This opens up great new ways for plugin developers to create migration solutions.
HSTS
HTTP Strict Transport Security (HSTS) protects your visitors (and you) from MitM-attacks like protocol downgrade attacks.
Whenever you type in for example omniploy.com into the address bar for the first time and visit the page, the first request is typically a plain text HTTP request. The server, however, responds with a redirect to the upgraded and encrypted HTTPS connection.
Now with HTTP Strict Transport Security (HSTS) the server tells the browser that every connection to this domain has to be through an HTTPS connection for a specified period of time.
HSTS is enforced in the browser if the server responds with the HTTPS response header field “Strict-Transport-Security”.
I’d recommend you to enable HSTS on your websites, however, it’s not as important nowadays. Many browsers (or extensions like HTTPS Everywhere) use HTTPS over plaintext HTTP now by default. So instead of first performing an HTTP request which redirects to the HTTPS version, the browser directly connects on HTTPS. This makes downgrade attacks much harder (although not impossible).
Protection of user accounts
When talking about WordPress security, we also have to talk about the protection of user accounts. In this section, we are going to talk about passwords, usernames, 2-factor authentication, and much more.
Strong passwords
Using strong passwords is an integral part of protecting user accounts from getting compromised. Hackers often employ sophisticated and automated brute force attacks to crack passwords.
Here are the three rules to strong passwords:
Strong password rule #1: Complex
A strong complex password complies with the following rules:
- At least 12 characters long.
- Both uppercase and lowercase letters.
- At least one number.
- At least one special character.
Strong password rule #2: Unique
Don’t use a password multiple times. Every account you create should have a unique password. This protects you from getting multiple accounts compromised when one leaks your password.
Where to store all my passwords?
Nobody can remember all passwords they use. Especially not if they are complex and unique for every account. We use 1Password internally, however, there are many solutions. Always check the reputation.
Strong password rule #3: Unknown
You should never use passwords that are somewhere in a database. Some services let you check your passwords against password databases. If it’s in a database, don’t use it!
Many password managers automate this process of checking your passwords. Passwords that fulfill the complexity rule shouldn’t have that much of a problem with being unknown. Still, it’s good practice to check.
Two-factor authentication (2FA) / Mulit-factor authentication (MFA)
Two-factor authentication or multi-factor authentication is a mechanism, in which you have to enter a second, for example, time-based, password to gain access.
There are different types of Multi-factor authentication. There is the time-based OTP (One-time-password), SMS, E-Mail, and more. I personally prefer time-based OTPs, since they always work are an open standard and can all be saved easily in a password manager.
Try to prevent using SMS or email for 2FA since they are often interceptable.
There are many 2FA plugins for WordPress. If you are already using something like Wordfence or iThemes Security, I’d recommend going with that, since it’s already included and doesn’t require any other plugins to be installed.
Default admin usernames
Try to always use something different for admin users than the default usernames like “admin”, “wpadmin”, “root”, or “administrator”. While this is not to make your admin username your second password, since WordPress sees usernames as part of public not private information, it still keeps a lot of the dumber bots from trying brute force attacks. These are the usernames that are targeted by most bots that try to enter the WordPress website. Use something random like “adm1n1strat0r”. It doesn’t really matter what you use, just don’t use common ones.
Plugins like Wordfence also have an option for forbidden usernames. If somebody tries to sign in with these forbidden usernames, they are blocked immediately from signing in. Forbidden usernames should be the typical admin usernames “admin”, “root”, “wpadmin”, etc.
Usernames are not for authentication so they should not be treated like passwords. This is more of a honeypot tactic.
Login limit
To make your WordPress websites less attractive to brute force attacks, you should limit the attempts somebody can try to log in. This makes it much harder for hackers to guess your password and increase website performance since requests are blocked after a certain number of tries.
Plugins that include login limits and blocks are Wordfence and iThemes Security. However, there are many more in the WordPress Repo.
Using admin users crrectly
You should use Admin users only if you really need the rights they have. For example to do maintenance work, install plugins, or create users. But if you only create pages or posts you should switch to a user with more restrictive rights like the Editor or Author role.
With plugins like the Members plugin, you can create custom roles with exactly the permissions required for the task.
Using different user accounts with different rights levels also makes you way less vulnerable to CSRF attacks.
Plugin and Themes in the WordPress security context
Plugins and themes play a big role when talking about WordPress security. Since they are, just as WordPress core itself, running plain PHP code, you should always check if you trust the developer. In this section, we are going to talk about what to look for in plugins and themes to keep your WordPress websites secure.
Unused plugins and themes
Plugins and themes that are not used should be removed completely. Don’t just deactivate them. Delete them. Plugin and theme files still can be called directly even when the plugin is deactivated. Fewer executable files on your server mean a smaller attack surface.
You can always install it later again.
Outdated and abandoned plugins and themes
You’ve probably seen this message on top of a WordPress plugin page once or twice. I’d highly recommend you not to use outdated and abandoned plugins since nobody is continuing development, leaving you without any patches to later found security vulnerabilities.
Down the road, this can even mean an update block. The outdated plugin is relying for example on a lower PHP version, which is not supported by a more recent plugin or even WordPress core itself anymore.
Also, try to switch to other systems as soon as you see that the plugin developer is abandoning development.
That’s also a reason why I recommend doing a trust check on every plugin you use. This will lead to way fewer problems down the road.
Reputation of the developer
As we learned throughout this WordPress security guide, the developer has a lot of power over our WordPress websites. Trust is a major factor here. The developers have the power to change code on your system with updates, meaning you have to trust the people behind the plugin or theme to not do something dumb.
Here are a few questions you should ask yourself before installing a plugin:
- Is the plugin/theme on the market for a long time?
- Does the developer commit to support the plugin in the long term?
- Does the plugin or theme bring a revenue stream to the developer to support expenses and to make the continuation of development worthwhile?
- Is the plugin or theme developed by a company or a large community?
- Has the plugin or theme had only few security vulnerabilities in the past? (Check the vulnerability database for plugins and themes)
- Has the developer a good track record in responding to reported vulnerabilities and patching them quickly?
- Has the developer a good track record on not breaking stuff when updating the plugin or theme?
If you can mostly answer these questions with yes, you are probably fine with using the plugin or theme.
Like with every piece of software you use or buy, you should check if you trust the developers behind it.
Server configuration and wp-config.php in the WordPress security context
How the server of your website is configured, can make a big difference in terms of WordPress security. Since server security can be a topic on its own, we focus here a bit more on the WordPress related part.
PHP execution in /uploads directory
The /uploads directory in WordPress is for media files. Something that doesn’t fit in there are executable files. To prevent executable files from running even if they get through the upload filters, you have to configure your web server engine (Apache) to not execute them.
Plugins like Wordfence provide an easy option to disable execution in the /uploads directory.
However, you can also do this easily manually. Simply put the following lines in a file called .htaccess directly in your /wp-content/uploads/ directory:
// .htaccess
<IfModule mod_php5.c>
php_flag engine 0
</IfModule>
<IfModule mod_php7.c>
php_flag engine 0
</IfModule>
<IfModule mod_php.c>
php_flag engine 0
</IfModule>
AddHandler cgi-script .php .phtml .php3 .pl .py .jsp .asp .htm .shtml .sh .cgi
Options -ExecCGI
This deactivates the PHP engine for the directory completely. This is pretty much the same as what the Wordfence plugin does if the option is enabled.
wp-config.php: The WordPress configuration
The wp-config.php is the configuration file for WordPress related stuff. You can find a complete reference for the wp-config.php and what configuration option it has out-of-the-box on the official WordPress website. In this security guide, we talk about the wp-config.php in the scope of WordPress security.
Moving wp-config.php one directory up
Many users of WordPress are unaware that the wp-config.php file may be moved one directory higher, and WordPress will still locate it. This is a pretty basic but powerful step, security-wise. Your wp-config.php normally is in the same directory as all the other ones. Meaning in the public space.
Now if you have configured everything correctly, there is usually not much to worry about since even if you’d call the wp-config.php directly by accessing yourwebsite.com/wp-config.php, it would still only show a blank page. This is why the config stuff is all in PHP and not echoed on the page.
However, if there is a misconfiguration on the server and the file gets returned showing PHP code, you can quickly get in trouble. Considering how important and secret the data in the wp-config.php is, we should always consider moving it out of the public space. It’s just an easy security win.
Disabling source code editing in the backend (DISALLOW_FILE_EDIT)
WordPress has a theme and plugin editor which can be used to directly edit plugin and theme files. I’d highly recommend you disable this.
First, editing source files is dangerous. One wrong character and your website can be down. Another problem is that when plugins and themes are updated, all edits to source code files are lost since the plugin/theme files get overwritten and thus all changes are gone.
There is only one case where I can think this backend code editor could be viable. And that’s when you have a child theme, and you quickly want to modify that. But also here it would be good practice to use a code editor and version the changes. That’s all not the case with the theme and plugin editor.
So for 99.9%, I’d recommend disabling the code editor completely.
And that’s pretty simple. Open up the wp-config.php file and add the following line:
// wp-config.php
// Below this line => /* That's all, stop editing! Happy blogging. */
define('DISALLOW_FILE_EDIT', true);
Forcing HTTPS on the backend (FORCE_SSL_ADMIN)
If your website communicates over HTTPS (which it should) you should set the FORCE_SSL_ADMIN option to true. This forces all traffic to the backend over HTTPS and allows the transfer of cookies only over HTTPS by setting the Secure flag on the cookie.
Add the following line to your wp-config.php to force HTTPS on the backend:
// wp-config.php
// Below this line => /* That's all, stop editing! Happy blogging. */
define('FORCE_SSL_ADMIN', true);
Unsing unique security keys
WordPress uses unique keys to sign and encrypt cookies. These keys should be kept secret since an attacker could generate session cookies with these keys themselves. If you follow the normal WordPress installation process or some other automated installation process, these keys should get generated automatically. But sometimes, especially in older WordPress installations, generating keys was often never done which let you with something like this:
// wp-config.php
// Never let your config with this
define( 'AUTH_KEY', 'put your unique phrase here' );
define( 'SECURE_AUTH_KEY', 'put your unique phrase here' );
define( 'LOGGED_IN_KEY', 'put your unique phrase here' );
define( 'NONCE_KEY', 'put your unique phrase here' );
define( 'AUTH_SALT', 'put your unique phrase here' );
define( 'SECURE_AUTH_SALT', 'put your unique phrase here' );
define( 'LOGGED_IN_SALT', 'put your unique phrase here' );
define( 'NONCE_SALT', 'put your unique phrase here' );
If you find this in your config, I’d highly recommend you generate new ones (WordPress.org has an easy endpoint for that) and reset all passwords.
Changing database prefix ($table_prefix)
The database prefix brings you the possibility, to run multiple WordPress instances in one database, simply by using different prefixes.
In terms of WordPress security, I’d recommend you to use something random as a prefix.
Changing the database prefix is an easy way to improve security on a new WordPress installation by simply changing the database prefix to something different than wp_. WordPress asks you while installing what you wish to use as a database prefix. Use something random, like c3po_.
You can also change the database prefix later. However, I’d recommend you to use some plugin for that and always create a backup before changing the database prefix.
Plugins like iThemes security allow you to change the database prefix afterward.
Blocking display of debug messages (WP_DEBUG_DISPLAY)
If you have to do debugging on a live site, don’t display debug messages directly to the screen. Debug messages can include information that could be potentially interesting for attackers. Rather let WordPress write into a debug.log file.
// wp-config.php
define('WP_DEBUG_DISPLAY', false);
define('WP_DEBUG_LOG', true);
define('WP_DEBUG', false); // Change this to true if you want to log debug messages.
You should set the WP_DEBUG_DISPLAY option to false right away, even if you don’t debug now. That way you don’t risk displaying any debug messages because you’ve forgotten to turn it off.
Web Application Firewall (WAF)
A web application firewall is like anti-virus software for your website. It sits in front of your website and checks for potentially harmful requests.
Plugin WAF
If your hosting provider doesn’t provide a WAF, you can also go with a plugin solution. Our favorite option is Wordfence. Besides real-time Firewall Rule updates and IP blacklists, they also provide numerous other features like blocking, throttling, 2FA, and much more to lock down your WordPress website.
However, we recommend the paid version since the free version gets most firewall rule updates for newly detected security vulnerabilities only after 30 days. This means if a vulnerability is detected in a plugin you’ve installed on your system, and you are not updating that plugin, your website is vulnerable for 30 days before you get the firewall rule.
However, I’d recommend you to enable the extended protection. That way Wordfence can check requests before WordPress itself is even loaded and protect you against plugin and theme attacks.
Cloud WAF
A cloud WAF is a proxy in between your WordPress website and your visitors. Instead of pointing the domain to the server directly, you configure the IP to point to a cloud WAF. This cloud WAF will then forward legit traffic to the server and back.
Examples of cloud WAFs are Cloudflare or Sucuri WAF.
Standalone WAF
A local server WAF is software installed locally which filters traffic before it reaches your WordPress website.
An example of a local server WAF is the NinjaFirewall. They specialize in WordPress and PHP. Instead of installing a plugin in the WordPress environment like with Wordfence, they place a standalone WAF in front of your webserver. Another example would be the 7G firewall from Perishable Press.
Security Plugins
The following list contains not all WordPress security plugins out there. These are the ones we used and reviewed ourselves and with them, you can pretty much do everything you need to do in terms of WordPress security.
Wordfence Security
Wordfence is our favorite security plugin. The people at Wordfence focus solely on WordPress security and do an awesome job of identifying and closing vulnerabilities in plugins and themes.
Wordfence Premium is a bit expensive but worth it if you want to keep your stress level low. And they get cheaper on every license. The price for Wordfence starts at $99 and goes down to $75.25 if you buy 15 or more licenses. You find a complete list of their prices on their website.
Besides their WAF which is developed solely for WordPress, they also include many more features to harden your WordPress security. Here is a list of a few features included in Wordfence:
- Rate limiting
- 2FA (2-factor authentication)
- Country blocking
- Automatic malware scanning
- Brute force protection
- And many more…
I’ve heard a few times in the WordPress community that they experience slow websites with Wordfence activated in their websites. I haven’t been able to confirm, that Wordfence is slowing down your website. I think this may be true if you run on extremely cheap and limited resources. However, you shouldn’t have problems if your hosting environment is properly scaled.
They also have a great podcast called Think Like a Hacker with Wordfence. They talk about current security events. If you want to stay up-to-date on what is going on in the WordPress security world, I can really recommend you to subscribe to their podcast.
iThemes Security
iThemes Security is a little bit of an allrounder. There is also an iThemes Security Pro option which extends the functionality even further. They however don’t provide a real-time updated WAF.
That’s also the reason why they can provide a plan for $199 per year for unlimited sites. Unlike Wordfence, they only sell the plugin and don’t need to provide infrastructure for every WAF installed.
I still think that iThemes Security, together with a plugin, cloud, or standalone WAF is a great way to greatly improve the security of your WordPress websites. Especially if the prices for Wordfence turn you off.
Other security plugins
There are many more security plugins out there that get the job done. I haven’t worked a lot with them. However, they are from reputable developers, so I have no problems recommending them.
- WPMU DEV Defender
- Jetpack
- Sucuri Security
Response to hacked WordPress websites
Up until now, we’ve learned how to protect our WordPress websites from malicious attacks. In this part, we are going to talk about what to do in case of a breach. Having a strategy in place helps to reduce stress and panic, as well as the time it takes to respond to and stop the attack.
The WordPress security incident response plan
A WordPress security incident response plan defines 6 phases for every incident.
Let’s take a look at these processes from the perspective of WordPress website developers and administrators.
Incident response #1: Prepare
Never think you are unhackable. There only has to be one severe zero-day vulnerability in a plugin or theme on your website that compromises the whole system. You should prepare for such cases. Think about backup systems and personnel training for administrators to fight off social engineering.
Here is a list of things to consider when preparing for hacking attacks:
- Having a solid backup solution. Onsite and offsite.
- Having a way to analyze logs.
- Training for clients that demand admin rights on their website.
- Having security policies in place. Who can do what and when?
- Having malware scanners in place.
Preparation is key when dealing with hacking attacks. It lowers the risk of panicking in such situations, which in turn means you are quicker in dealing with them. Preparation also often decides if you even detect the attack.
If a hacker only wants to take over your website for their DDoS network, you may never know that your website is compromised or only after it’s too late.
Incident response #2: Identify
In order to fight off an attack, you first have to identify what is being attacked. That’s where logs come in and why I recommended in the preparation part to have a way to analyze logs. They help you to identify what endpoints are called and their payloads.
Another way to identify attacks is to use a malware scanner. Configure a malware scanner to run every few days and to inform you in case of malware is found. For example, Wordfence allows you to do this.
Also, check if other websites or systems are impacted by the attack. If you have identified the vulnerable piece, like a plugin, check all your other systems and if you can, disable it, to prevent another attack on these systems.
Incident response #3: Contain
When identified you have to contain the malware. Back up the infected WordPress website, change all security keys, API keys, passwords, etc. If possible take it off the network. Update the software and PHP versions on the webserver.
You want to do everything to get it under control.
Incident response #4: Eradicate
When contained, you need to eradicate the root cause. This can be removing an insecure plugin, changing breached passwords, updating outdated software, and so on.
You also have to check if a backdoor has been installed. Hackers do that exactly for that case that you fix the initial hole or to gain even more power over your systems.
Incident response #5: Recover
If possible move your cleaned website to a fresh and updated webserver. Be sure that you are securely protected against a new breach when you go back online.
Have you changed all passwords and API keys in the compromised website? Did you remove all unknown user accounts created by the attacker? Is the core, the plugins, the theme up-to-date?
Also, define how long you want to monitor the recovered WordPress websites more closely.
Now it’s also the time to improve and harden the security of your WordPress websites. Introduce WAFs, 2FA, more strict password policies, and policies to software used. Check all used plugins and themes, and if they don’t comply with these new policies, replace them with something else.
Incident response #6: Lessons learned
If a plugin or theme was responsible for the breach, notify the developers about that vulnerability. They should quickly fix the problem to prevent other WordPress websites from getting hacked.
If any personal information on your visitors or users was leaked, you are obligated in most countries to report such incidents to the authorities and inform the affected people. The logs help you identify the leaked data.
Also, ask yourself what you should do differently in the future to prevent such situations. For example, after a social engineering attack against a client’s website, check if clients have to be trained better.
Even if you never need this response plan, you should still have one. It removes stress, and it’s also a lot more professional towards your clients.
Anti-patterns in the WordPress security context
There are a few WordPress security anti-patterns that still get practiced and taught in the industry. Anti-patterns are ineffective or even counterproductive solutions that are frequently employed in reaction to a problem. This section contains some personal opinions, so read it and decide for yourself whether you want to use them.
Security by obscurity
I’d consider security by obscurity, not good practice. At least for WordPress websites. Either because it doesn’t have much of an impact on the security of your website, because it could create problems with certain plugins or themes, or because it could give you a false sense of security.
There are plugins that try to hide that your website is running WordPress. They do this by trying to remove everything that could lead someone to the conclusion that you are running on WordPress. Like by changing the default /wp-admin path to something different, or by removing the WordPress meta tags.
However, this could lead to problems with other plugins or themes that expect that data to be there. Also, every plugin that loads something on the frontend could be used to detect that it’s WordPress by its own unique thumbprint.
Do not rely on security by obscurity.
It is way too much effort for the benefits you get. As I said you’d have to check every plugin and theme if they leave something in the frontend that could expose your WordPress installation. And for what? That nobody knows you are running on WordPress? Not worth it. If you follow the rules above you are already secure.
Captchas on login forms
If you followed the section on protecting user accounts you should already be well suited against common login attacks. On login forms, captchas make your and your user’s life harder without adding much benefit. Additionally, if you choose to use reCAPTCHA you also send valuable data to Google for free.
Captchas are useful for contact forms, signup forms, and alike but not for sign-in forms.
Other great WordPress security related resources
WordPress security is not a set it and forget it topic. You should always stay on top of this topic. Only that way, you know when and how to react to new threads.
Blogs and websites
There are many other great blogs and websites online which cover WordPress security.
Podcasts
Security podcasts are great to keep you updated on security-related topics and what is going on in this world. I really like to listen to them on my commute to the work or while doing housework.
Top comments (2)
hi there - good day.
this is very very in depth going,.
Thank you for writing this bro. This was really helpful. i like this list alot. Thank you for supporting the WP-Community.
keep up the great work - it rocks
btw:- we look forward to see more support for the upcoming WordPress Version 5.9
with all the great features like
Full Site Editing (FSE)
Query Loop
Block-Theming
Gutenberg etc. etx.
Great things and enhancements ahead. The global WordPress-Community is waiting for this. And is curious about some news that cover all the good news. 🙂
Amazing article! It should be read by all plugin and theme developers...lots of plugins don't even check for nonces :)
Some comments may only be visible to logged-in visitors. Sign in to view all comments.