WordPress security doesn’t have a good reputation. More than 70% of all WordPress sites carry some kind of vulnerability according to research done on +40.000 WordPress sites by Alexa. If you develop WordPress themes or plugins — or use WordPress for your websites — that number should scare you.
There’s a lot you can do to make sure you’re not part of the 70%, but it takes more work than just installing a plugin or escaping a string. A lot of advice in this article comes from Sucuri’s guide on WordPress security and years of personal experience.
Is WordPress Insecure?
WordPress has the largest market share among content management systems and a 30% market share among the most popular 10 million sites on the web. That kind of success makes it a big target for hacks. WordPress isn’t less secure than other content management systems — it’s just more successful.
Vulnerabilities in WordPress core are responsible for less than 10% of all WordPress hacks. Most of those are from out-of-date WordPress installs. The amount of hacks that happen on actual security holes in up-to-date versions (also known as zero-day exploits) in WordPress core account for a tiny percentage of all hacks.
The rest of the infected sites were caused by plugins, themes, hosting, and users. And you, as a WordPress website developer, have control over all of those. If this seems like a big hassle to you, then I can recommend Sucuri’s agency plan. Otherwise, let’s find out how to deal with WordPress security ourselves!
Who’s Attacking You And Why?
Let’s bust a myth first: A small WordPress website is still an attractive target for hackers. Attacks on a personal basis are very rare. Most hacked WordPress websites are compromised automatically by either a bot or a botnet.
Bots are computer programs that constantly search for websites to hack. They don’t care who you are; they just look for a weakness in your defences. A botnet combines the computing power of many bots to tackle bigger tasks.
Hackers are primarily looking for a way into your server so that they can use your server’s computing power and turn it loose on some other goal or target. Hackers want your server for the following reasons.
SENDING SPAM
Spam accounts for about 60% of all email, and it has to be sent from somewhere. Many hackers want to gain entry to your server through a faulty plugin or an ancient version of WordPress core so that they can turn your server into a spamming machine.
ATTACKING OTHER WEBSITES
Distributed denial-of-service attacks use many computers to flood a website with so much traffic that they can’t keep up. These attacks are very difficult to mitigate, especially when they are done right. Hackers who break into your server can add it to a pool of servers to attack websites.
STEALING RESOURCES
Mining cryptocurrency is very popular now, but it takes a lot of computing power. Hackers who don’t want to spend a lot of money on a server farm will break into unprotected WordPress websites and gain access to servers or to your websites’ visitors and steal computing power.
BUMPING SEO SCORES
A particularly popular hack for WordPress is to gain access to its database and add a bunch of (hidden) text underneath each post, linking to another website. It’s a really quick way to bump one’s SEO score, although Google is getting more vigilant about this behavior, and blacklistings are increasing.
STEALING DATA
Data is valuable, especially when it’s linked to user profiles and e-commerce information. Getting this data and selling it can make an attacker a handsome profit.
Why Does Security Matter?
Apart from not giving criminals the satisfaction, there are plenty of reasons why your website should be secure by default. Having cleaned and dealt with plenty of WordPress hacks myself, I can surely say that they never occur at a convenient time. Cleaning up can take hours and will cost either you or your client money.
To get a hacked WordPress website up and running again, you’ll need to remove and replace every bit of third-party code (including WordPress core); comb through your own code line by line and all other folders on the server to make sure they are still clean; check whether unauthorized users have gained access; and replace all passwords in WordPress, on your server and on your database.
Plenty of services can clean up a WordPress website for you, but prevention is so much better in the long run.
Apart from the cost of cleaning up, hacks can also cost you a lot in missed sales or leads. Hacks move you lower in search rankings, resulting in fewer visitors and fewer conversions.
More than the financial cost, getting hacked hurts your reputation. Visitors come to your website because they trust you. Getting hacked damages your reputation, and that takes a long time to repair.
There’s also a real possibility of legal issues, especially if you have customers in the EU, where GDPR legislation will go into effect in the summer of 2018. That new legislation includes a hefty fine for data breaches that aren’t handled properly.
Money, reputation and legal problems: Bad security can cost you a lot. Investing some time in getting your website, code and team set up with a mindset of security will definitely pay off.
Let’s find out how we can prevent all of this nastiness.
The CIA Triad
The CIA triad is a basic framework for every digital security project. It stands for confidentiality, integrity and availability. CIA is a set of rules that limits information access to the right parties, makes sure the information is trustworthy and accurate, and guarantees reliable access to that information.
For WordPress, the CIA framework boils down to the following.
CONFIDENTIALITY
Make sure logged-in users have the right roles assigned and that their capabilities are kept in check. Only give users the minimum access they need, and make sure that administrator information doesn’t leak out to the wrong party. You can do so by hardening WordPress’ admin area and being careful with usernames and credentials.
INTEGRITY
Show accurate information on your website, and make sure that user interactions on your website happen correctly.
When accepting requests on both the front and back end, always check that the intent matches the actual action. When data is posted, always filter the data in your code for malicious content by using sanitization and escapes. Make sure spam gets removed by using a spam protection service such as Akismet.
AVAILABILITY
Mak sure your WordPress, plugins and themes are up to date and hosted on a reliable (preferably managed) WordPress host. Daily automated backups also help to ensure that your website stays available to the public.
All three elements lean on each other for support. Code integrity will not work on its own if a user’s confidential password is easily stolen or guessed. All aspects are important to a solid and secure platform.
Security is a lot of hard work. Apart from the work that can be done in code, there’s a huge human element to this framework. Security is a constant process; it can’t be solved by a single plugin.
Part 1: Integrity — Trust Nothing
Verify the intent of user actions and the integrity of the data you’re handling. Throw your inner hippie out the door. Nothing can be trusted online, so double-check everything you do for possible malicious intent.
DATA VALIDATION AND SANITIZATION
WordPress is excellent at handling data. It makes sure that every interaction is validated and that every bit of data is sanitized, but that’s only in WordPress core. If you’re building your own plugin or theme or even just checking a piece of third-party code, knowing how to do this is essential.
In this example, we’ve added two pieces of data to a WordPress post using update_post_meta. The first is a string; so, we cast it as a string in PHP and strip unwanted characters and tags with sanitize_text_field
, one of WordPress’ many sanitization functions.
We’ve also added an integer to that post and used absint to make sure this is an absolute (and non-negative) integer.
Using core WordPress functions such as update_post_meta
is a better idea than using the WordPress database directly. This is because WordPress checks everything that needs to be stored in the database for so-called SQL injections. A SQL injection attack runs malicious SQL code through the forms on your website. This code manipulates the database to, for instance, destroy everything, leak user data or create false administrator accounts.
If you ever need to work with a custom table or perform a complicated query in WordPress, use the native WPDB class, and use the prepare
function on all your queries to prevent SQL injection attacks:
$wpdb->prepare goes through every variable to make sure there’s no chance of a SQL injection attack.
ESCAPING
Escaping output is just as important as sanitizing input. Validating data before you save it is important, but you can’t be 100% sure it’s still safe. Trust nothing. WordPress uses a lot of filters to enable plugins and themes to change data on the fly, so there’s a good chance that your data will get parsed through other plugins as well. Escaping data before adding it to your theme or plugin is a smart thing to do.
Escaping is mainly meant to prevent cross-site scripting (XSS) attacks. XSS attacks inject malicious code into the front end of your website. An added bonus of escaping data is that you can be sure that your markup is still valid afterwards.
WordPress has many escaping functions. Here’s a simple example:
Escape as late as possible. This ensures that you have the final say over your data.
SECURING REQUESTS
WordPress admin requests are already pretty secure if you have SSL enabled and if you have a decent host, but some vulnerabilities still exist. You need to check a user’s intent and validate that the incoming request is something that was done by the actual logged-in user.
WordPress validates intent with nonces.. A nonce (or “number used only once”) isn’t really an accurate description of this API in WordPress. It doesn’t only use numbers, and it is much more like a cross-site request forgery (CSRF) token that you’ll find in every modern web framework. These tokens make sure hackers can’t repeat requests. It’s a lot more than just a nonce, but WordPress likes backwards-compatibility, so the name stuck.
Nonces are sent along with every vulnerable request that a user makes. They’re attached to URLs and forms, and they always need to be checked on the receiving end before performing the request. You can add a nonce to a form or a URL. Here’s an example used in a form:
In this case, we’re just using the simple helper function wp_nonce_field()
, which generates two hidden fields for us that will look like this:
The first field checks intent by using a generated code with the 'post_custom_form'
string that we’ve passed to the function. The second field adds a referrer to validate whether the request was made from within the WordPress installation.
Before processing your task on the other end of the form or URL, you would check the nonce and its validity with wp_verify_nonce
:
Here, we’re checking the nonce with our action name, and if it doesn’t match, we stop processing the form.
THIRD-PARTY CODE
Third-party plugins and themes are a hotbed for hacks. They’re also the toughest nut to crack when ensuring the security of your website.
Most WordPress hacks are caused by plugins, themes, and out-of-date copies of WordPress. No piece of software is 100% secure, but a lot of plugins and themes out there either haven’t been updated in a while by their developers or weren’t secure to begin with.
Less code means less to hack. So, before installing yet another plugin, ask yourself whether you really need it. Is there another way to solve this problem?
If you’re sure you need a plugin or theme, then judge it carefully. Look at the rating, the “last updated” date and the required PHP version when browsing through WordPress’ plugin directory. If you’ve found what you’re looking for and everything seems to work, search for any mentions of it on a trusted security blog, such as Sucuri or WordFence.
Another option is to scan the code and make sure it contains proper nonces, sanitation and escaping; these are usually signs of well-written and secure code. You don’t have to know PHP or do a complete code review. A simple and quick way to verify proper use of WordPress security functions is to search the plugin’s code for these strings:
esc_attr
esc_html
wp_nonce_field
wp_nonce_url
sanitize_text_field
$wpdb->prepare
A plugin could still be secure if it doesn’t include all of these strings, but if none or a low number of these strings are found, that is a red flag. If you do find a vulnerability, please share it with the creator in private, and allow them time to fix it.
Keeping track of vulnerabilities in the WordPress plugin space is getting easier with initiatives such as wpvulndb.
Note: Some themes out there bundle versions of plugins with their code. This is a symptom of WordPress not having great out-of-the-box dependency management, but it’s also a sign of a very poorly written theme. Always avoid these themes because they include code bases that can’t be updated.
Themes and plugins rarely contain code written by only one developer. Composer and NPM have made it so much easier to depend on other libraries that it’s become a popular attack vector. If you’re downloading a cut-and-dry WordPress theme or plugin, this really isn’t a concern, but if you’re working with tools that use Composer or NPM, then it doesn’t hurt to check their dependencies. You can check Composer dependencies with a free command-line interface (CLI) tool by SensioLabs. A service such as Snyk (which you can use for free but which also has premium options) enables you to check every dependency in your project.
Part 2: Availability: Keep It Simple
Your main goal is to keep your website online without interruptions. Even with top-notch security, you can still get in trouble. When that happens, a great backup will save you a big headache.
UPDATES
Open-source can’t exist without updates. Most attacks on WordPress websites happen on outdated versions of either the core software or plugins. Security updates to WordPress’ core are now dealt with automatically (unless you’ve disabled this, you monster!), but security updates in plugins are a different story.
Updating is normally safe with popular, trusted plugins, but all plugins should be tested before they go live on your website. Tools such as WP CLI make updating everything much easier. WordPress lead developer Mark Jaquith had an excellent blog post on updating all plugins automatically yet gradually, so that you can filter out possible errors.
USERS, ROLES AND CAPABILITIES
“Availability” in the CIA triad has to do with getting information in the right hands. Our main priority with this is limiting the capabilities of your back-end users. Don’t give everyone an admin account.
The admin account in WordPress is unusually powerful. There’s even an option in vanilla WordPress to alter your complete code base from within the WordPress admin account. (If this is new to you and you haven’t disabled this, please do.)
The roles and capabilities system in WordPress is powerful and is very easy to alter in code. I create a lot of new roles when working with WordPress. The main benefit of this is that you get full control over which parts of the system various users get to access, but another huge benefit is that it prevents third-party code from altering the standard capabilities of WordPress core.
WordPress usually handles email via the server it’s on, but this makes all of your email completely dependent on the server it’s running on. Prevent your emails from getting intercepted and seen as spam by using an SMTP service. A lot of plugin options are available to make sure that all of your mail is sent over a secure SMTP connection.
You will, however, need access to the domain name’s DNS settings to add a Sender Policy Framework (SPF) record. All good SMTP services will provide the exact record that needs to be added. An SPF record ensures that your SMTP service is authorized by the domain to send email in its name.
MONITORING
Monitoring your website online is a 24/7 task that can be fully automated. In the case of WordPress, we’re interested in uptime and file integrity.
Monitoring uptime is usually something a good host will do for you. Tools such as Uptime Robot add even more security. Your first 50 websites are completely free.
Regarding file integrity, if a hacker gains access to your server they can change your code.
In this case, plugins are the answer to your problem. Sucuri has a great auditing plugin. It checks all files in your installation against a vast database of known malicious code. It also checks whether WordPress core is still 100% WordPress core, and it gives you a heads up if there’s been a breach, so that you can fix it as soon as possible.
BACKUPS
The ultimate fail-safe of every security process is automated backups. Most good hosts will do this for you, but there are other good options if your host doesn’t offer backups. Automattic makes one named VaultPress, and tools such as BackupBuddy back up to a Dropbox account or an Amazon S3 bucket.
Most of the reliable services in the WordPress backup space are either premium services or premium plugins. Depending on whether you need to fully control your data, you might prefer a plugin that comes with a cloud host, instead of a service. Either one is worth every penny, though.
HOSTING
WordPress isn’t the only piece of software running on your server. Plenty of attack vectors are open when you’re on crappy hosting. In fact, bad hosting is the main reason why WordPress still supports outdated versions of PHP. At time of writing, WordPress’ own statistics page reports that 32.5% of all WordPress installations are running on PHP versions that do not receive security updates anymore.
Note the almost 60% of installations running on PHP 5.6 and 7.0, which will receive security patches only until the end of this year.
Hosting is important not only for keeping your server’s software up to date, though. A good host will offer many more services, such as automated daily backups, automated updates, file-integrity monitoring and email security. There’s a big difference between managed WordPress hosts and hosts that give you an online folder with database access.
The best advice is to find a decent managed WordPress host. They cost a little more, but they provide a great backbone for your WordPress website.
Part 3: Confidentiality
If you’ve made sure that your code base is as secure as it can be and you’re on a great WordPress host, surrounded by malware scanners and backups, then you’re still going to experience security problems, because people are the worst… at Internet security.
Confidentiality is about educating yourself, your client and the users of the website.
CONFIDENTIAL DATA
You might not know it, but your plugins and themes are probably showing valuable confidential data. If, for instance, you have WP_DEBUG
set to true
, then you’re showing every hacker your website’s root path on the server. Debugging data should have no place in your production website.
Another valuable data source are comments and author pages. These are filled with usernames and even email addresses. A hacker could use these in combination with a weak password to get into your website. Be wary of what you show the outside world.
Also, double-check that you’ve put wp-config.php
in your .gitignore
.
DON’T CODE ALONE
A way to prevent a lot of mistakes from sneaking into your code base is to practice pair programming. If you’re by yourself, this a lot harder, but many online communities are available that are willing to do quick code audits. WordPress for instance, uses Slack to communicate everything about the development of its platform. You will find a lot of people on there who are willing to help. Slower but better alternatives are the WordPress forums, StackOverflow and GitHub Issues, where your questions (and their answers!) are saved so that other people can benefit from them.
Asking for input can be tough, but people love showing their expertise, and WordPress in general has a very open and welcoming community. The point is that if you never ask for input on the quality of your code, then you will have no idea whether your code is secure.
LOGINS AND PASSWORDS
Your clients will need to log into WordPress to manage their content. WordPress core does what it can to prevent weak passwords from getting through, but this usually isn’t enough.
I’d recommend adding a plugin for two-factor authentication to your website, along with a limit on login attempts. Even better, do away with passwords entirely and work with magic links.
TRUST BUT VERIFY
So far in this article, we haven’t talked about social engineering at all. It’s a form of hacking that’s gaining momentum, but it generally isn’t used to hack into WordPress websites. It is, however, an excellent way to set up the culture around your website with security in mind. That’s because the best defense against social engineering is “Trust but verify”.
Whenever a client, a user or your boss asks for something related to security, the best way to deal with it is to trust but first to verify whether what they are saying is true.
A client can claim they need administrator access to WordPress, but your job is to verify whether this is true. Do they actually need access, or are they missing just a single capability in their role? Is there a way to solve this problem without adding possibly new attack vectors?
“Trust but verify” is a simple yet effective mantra when it comes to security questions, and it can really help get people up to speed.
Conclusion
Is WordPress insecure? No, it’s not. WordPress core is constantly being updated and fixed, and most reported WordPress hacks aren’t from WordPress itself. Is the culture surrounding WordPress insecure? You betcha!
But by having security in mind with every line of code you write, every user you add, every plugin you enable and every hosting bill you pay, you can at least ensure that you’re running a secure website that keeps your reputation intact and your data safe.