WordPress powers over a quarter of the Internet. That’s quite a statement for a platform that began its life as a fork of a blogging engine. It’s also quite refreshing since WordPress is the reason I learned to write code in the first place.
One of the reasons WordPress is so popular is because it’s so easy. It’s easy to use as a writer. It’s easy to manage as a site administrator. It’s easy to code as a developer. This learning curve associated with WordPress is relatively flat – many devs and users can dive right in and get something functional from day 1 with little to no outside help.
Another reason for WordPress’ popularity is its long memory. WordPress has been around for over a decade, and the core development team has always prioritized backwards compatibility with the platform. Users of older versions of the software can upgrade to the latest version with, often, no loss in functionality.[ref]I personally upgraded a WordPress 2.3 site to 4.0 once. For context, version 2.3 came out 7 years before version 4.0. Still, the upgrade went flawlessly with zero lost content. The old theme even still worked on the newer platform![/ref]
Unfortunately, this long tenure also means that many in the community have a long memory of WordPress as well. They remember the days before plugins. The days before CSRF tokens were in common use throughout the codebase. The days when everyone just assumed WordPress was insecure by default.
Is WordPress Insecure?
Along a similar vein, WordPress is still very insecure.
At conferences I often remind people that the core code that powers WordPress is, in itself, vetted, secure, and reliable. The insecurities in code arise when administrators add a custom theme or start installing unverified third-party plugins. Any time you add someone else’s code to the system, you add a potential inroad for code-level vulnerabilities.
That being said, there are also specific platform-level risks[ref]I use the term “risk” instead of vulnerability here because each of these potential issues requires specific expertise to exploit. They’re also easy to defend against in a rigorous system. They are still risks, though, because it’s even easier to fall prey to a hack merely by being lazy or misreading a configuration tutorial.[/ref] that affect the system.
Among the various risks presented by WordPress, there are four in particular that I see most often in conversation:
Insecure Logins
A few versions back, WordPress removed the default “admin” username and started enforcing stronger passwords for authentication. These are both good changes to make, but don’t necessarily take things far enough. WordPress does not ship with a multi-factor authentication system. The core team has been working to build one; unfortunately the current solution bundles so many potential options as to paralyze site administrators. They can choose anything from FIDO U2F to Google Authenticator to an email-based system.
It’s a good start, and will definitely help secure WordPress logins, but it’s bringing too many options to the table.
Insecure Emails
If you forget your WordPress password, you can easily request a reset link from your site by providing either your username or your email address. The downside with this functionality is that anyone with access to your inbox can then use this link to reset your account.
You use a different password for Gmail and for your blog? Great! Unless someone steals your Gmail password … then they can also steal access to your WordPress account.
Insecure Front-ends
Many WordPress sites are rocking HTTPS thanks to Let’s Encrypt and the awesome integrations managed WordPress hosts are forming with the project. Sadly, just as many sites are failing to prevent mixed content or are trusting the scripts on their site to CDNs or other remote providers who may or may not be injecting malicious code. The sheer number of people who recommend using a CDN-hosted jQuery versus the version that ships with WordPress[ref]These two are not equivalent. WordPress enables “no conflict mode” by default by adding a line to their own script![/ref] shows how often developers are lead to trust an outside party to load executable code onto their site.
Insecure Upgrades
WordPress ships all updates (core, themes, and plugins) from a central server, but does nothing to ensure the integrity of the code downloaded. On the one hand, if the central WordPress.org server distributing updates were ever breached, more than a quarter of the Internet would be vulnerable to malicious software.
On the other hand, if your server is behind any sort of proxy and that proxy is attacked, whether or not WordPress.org is shipping rock-solid code becomes a moot point. There’s still no way for you to verify the proxy didn’t modify the payload before you received it.[ref]For all the talk about why update signing isn’t a priority, the risk of a hacked proxy is significant to anyone running on a managed host, in an enterprise environment, or retrieving data through any sort of cached proxy (or external update system like Packagist).[/ref]
Changing the Conversation
I’m tired of people lamenting the state of security with WordPress and doing nothing to address it. To be certain, there have been some significant efforts to address these security risks, but they either haven’t landed or have been de-prioritized to the point of obscurity. That’s a major disappointment, but I’m not going to just sit back and do nothing.
Instead, I’m going to open source the solutions I’m personally building to help fix the holes and turn the conversation around.
Insecure Logins
I continuously beta test various login security plugins to see how they can help average users stay more secure. The Two Factor plugin the core team is kicking around is great for people who know what they’re doing and want multiple options for authentication. I like it because I can use it with a Yubikey. I don’t like it because most users don’t even know what a Yubikey is.
I started with that plugin as a core, but reworked it to focus just on using a time-based OTP app and released a simplified fork named Dovedi. This plugin uses a lot of the same techniques, but is tested to work with everything from Google Authenticator on Android to Windows Authenticator on Windows Phone.[ref]The hash spec is implemented roughly the same for both, but Windows requires a longer key than Google does. I needed to increase the byte length of device secrets to gain universal compatibility.[/ref] I wanted to target technology many users already have – even if they’re not already using an Authenticator app, it’s something portable enough they can use it with other systems as well.
Dovedi was my first step in helping to make WordPress secure.
Insecure Emails
I’ve written extensively elsewhere on the need for and ease of configuring encrypted email systems. I use both GPG and S/MIME myself to ensure messages are signed whenever I send them and encrypted when they need to be. Once you have an email client capable of receiving and decrypting messages, the next trick is getting WordPress to send encrypted messages.
This past weekend, I released a new plugin called Secure Messaging that uses GPG to automatically encrypt password reset emails before they’re sent to a user for reclaiming their account. Once you’ve given WordPress your GPG public key, no one but you (with your private key) can ever steal your account, even if they somehow manage to hack your inbox.
Insecure Front-Ends
One of the coolest tools I’ve seen lately is Mozilla’s Observatory. This tool scans your site for compliance with many different security features:
- A content security policy that whitelists the domains allowed to serve scripts, images, fonts, and iFrames
- Whether or not your site properly implements HTTPS and if it strictly prevents TLS downgrade
- Whether or not your site implements sub-resource integrity for remote scripts
- If your site is protecting against cross-site scripting or frame-injection attacks
- And so on
By default, it’s difficult to get WordPress to score well under these tests.[ref]My “dev journal” on WordPress.com, for example, only scores a D where an A+ is possible. If WordPress.com is the “gold standard” for WordPress hosting, we’ve obviously got a lot of work left to do. This site, hosted on WP Engine at the time of this writing, does even worse with an F.[/ref] I’m collaborating with a couple of other developers to flesh out a plugin that helps configure a WordPress site to score much better according to these scans.
Remember, a higher score means a more secure front-end and a safer experience for your customers.
Insecure Upgrades
It doesn’t look like the core team will be cryptographically signing updates or payloads any time soon. That being said, I still want to ensure sites I interact with are receiving vetted, secure code. To that end, I will be publishing a list of signed hashes for core files (and a handful of plugins and themes) myself.
I will also be writing a plugin to force the WordPress updater to verify these hashes before completing the upload.
This obviously won’t fix a hacked site, and it won’t protect against all possible avenues of attack. However, it will help ensure that critical code being download through an automated update (via a proxy, for example) is guaranteed by me to be free from outside manipulation.
Next Step: Profit?
All of this is a labor of love. The tools above are, like almost everything else I do, freely available as open source. Where possible, I license under the terms of the MIT license (but use the GPL on tools like Dovedi, which are forked from others). All of the above constitute a fair amount of work on my part, but my goal is to provide these tools to the community and help change the conversation about WordPress and core security.
If you want to help, feel free to reach out to me on Twitter. If you want to contribute financially, donations are always gladly accepted.
The best way to help, though, is to raise awareness and offer feedback. Where are the rough edges in WordPress or in the systems that secure it? What could be done to make the “secure” way to do something the “easiest” way?