Like the blog? Get the book »

Securing the WP REST API

Securing the WP REST API

I think many WordPress users probably underestimate the amount of data that is made available via the REST API. Just about everything is available to anyone or anything that asks for it: posts, pages, categories, tags, comments, taxonomies, media, users, settings, and more. For most of these types of data, public access is useful. For example, if you have a JSON-powered news reader, it can basically replicate your entire site structure virtually anywhere. But that easy access invites potential abuse. Just like with RSS feeds, RESTfully delivered JSON content is easily scraped and used for spam, phishing, plagiarism, adsense, and other foul things.

User Data = Public Domain?

Beyond content theft, plagiarism, and such, the REST API opens the door to another potential security slash privacy concern over user data. By default every WordPress site delivers a significant amount of user data to anyone or anything that asks for it. For any user (of any role) that is the author of at least one post, their personal information is openly available to literally everyone.

So exactly which user data are exposed via the REST API? As explained in the documentation, the /users endpoint delivers basically everything except for user email addresses and passwords. Everything else — ID, Name, Website, Description, URL, Metadata and more — all public domain thanks to REST API.

To give you a more concrete example of the data that is shared publicly via the REST API, consider the following URL:

https://digwp.com/wp-json/wp/v2/users/3

Here we are invoking the REST API by calling a specific user endpoint (i.e., user ID = 3). Requesting that URL in a browser, the following data are returned:

{
	"id": 3,
	"name": "Jeff Starr",
	"url": "https:\/\/perishablepress.com",
	"description": "Jeff Starr is a professional web developer and book author with over 15 years of experience...",
	"link": "https:\/\/digwp.com\/author\/jeffstarr\/",
	"slug": "jeffstarr",
	"avatar_urls": {
		"24":"https:\/\/secure.gravatar.com\/avatar\/...",
		"48":"https:\/\/secure.gravatar.com\/avatar\/...",
		"96":"https:\/\/secure.gravatar.com\/avatar\/..."
	},
	"meta": [],
	"_links": {
		"self":       [{"href":"https:\/\/digwp.com\/wp-json\/wp\/v2\/users\/3"}],
		"collection": [{"href":"https:\/\/digwp.com\/wp-json\/wp\/v2\/users"}]
	}
},

This same information also is available at other endpoints, for example:

https://digwp.com/wp-json/wp/v2/users

There you will find the same amount of information provided for every qualified user. Note that you can try this on your own WordPress-powered site. Simply replace digwp.com with your own domain name, and remember to include the subdirectory path if WordPress is installed in a subdirectory.

What’s the Risk?

So WP REST API and security. For everything except the user data, the main risks basically are the same as for RSS feeds. Scrapers and content thieves are savvy enough to steal your content regardless of format. If you make it easy for people to steal your content, they will. So whether they’re grabbing the data via RSS or JSON format, content is content, and the REST API makes it easier than ever for anyone and anything to manipulate your site’s content, categories, tags, meta, and much more. Is that acceptable? Totally your call.

Now for user data, we enter a whole new level of risk. With user data, the information is personal, so there is a potential privacy risk. Even worse, for every user, their “Name” by default is their “Display Name”, which defaults to the registered Username unless otherwise specified. This means that your site’s registered usernames are publicly available, so there is a potential security risk.

Privacy Risk

For the privacy risk, perhaps it is a non-issue for most WordPress sites. But for the percentage of sites that must abide by an official privacy policy or other company rules and regulations (GDPR, anyone?), publicly sharing information about every qualified user is gonna be a problem. Or maybe your site needs to keep all author information private for legal or political reasons (like at a news reporting or government site). In many such cases REST’s default functionality may present serious privacy risk. As someone said somewhere on social media1:

A lot of institutions use WordPress for their staff or students or even patients/clients. They probably have no idea this is exposed and they also probably have some level of security policy that doesn’t allow names to be listed publicly. I think it’s a problem and should be opt-in.

Security Risk

For the security risk, the significance and extent of the issue is up for debate1,2,3. In general, bad actors require at least two things to gain access to your site4:

  1. Username
  2. Password

And thanks to the WP REST API, they now have half of what they need. So the REST API introduces a security vulnerability by making it easier for attackers to brute-force their way into your site5,6. Instead of having to guess the correct username AND password, now they just have to guess the password. Which unfortunately for many user accounts is just too ridiculously easy to do.

Another on-topic post from social media1:

Its one-half of the puzzle in acquiring unauthorized credentials. If you’re trying to follow best practices, you don’t expose sensitive data. If you’ve worked to remove all leakage of user names from your site, this just re-exposed all that data.

How to Secure the REST API

So at this point, you should have a pretty good understanding of how the WordPress REST API works and why it can be considered a privacy and/or security risk for probably a vast percentage of WordPress sites. Now you get to decide whether or not it is necessary to take action and secure your site against unsafe data exposure. Fortunately, there are a couple of easy ways to lock it down using a WordPress plugin. Here are a couple of free options:

Full disclosure, the first option listed here, Disable WP REST API, is one of my own plugins. It is designed to be super lightweight and effective. That in mind, either of these plugins is gonna do the job to protect against unwanted REST exposure. If you know of other/better techniques, share ’em in the comments.

WordPress Team On Point

The WordPress team is aware of this potential privacy/security risk and already has taken steps to lock it down. For example, before the WP 4.7.1 update, the REST API exposed sensitive data for ALL registered users, regardless of whether or not they are credited as Author for any post(s).

So thanks to improvements made in version 4.7.1, WordPress now displays user data ONLY for users (of any role) who are credited as author for registered post types. This important step helps to reduce user data exposure, and tells us that the WP team is actively working to keep WordPress as safe and secure as possible. Hopefully they will take further steps to eliminate unnecessary exposure of sensitive user information.

Closing Thoughts

As simple as it is to properly address the fundamental vulnerabilities inherent in the WP REST API, unfortunately most WordPress users will remain blissfully unaware and do nothing. This is why the REST API should disable the public view of most if not all user data.

Sensitive information should be exposed only to authenticated users. Disabling exposure of user data by default helps to protect the vast majority of WordPress users, and of course developers always will be savvy enough to enable the user data endpoints if/when needed. It’s a win win! :)

Footnotes

Here are some related materials and resources FYI:

11 responses

  1. Antony de Navarro

    A useful, and timely, article Jeff.

    A shame that the WP team don’t see this as more of an issue. Not least I have noticed an increase in the number of brute force attacks using my not-published admin username on several sites.

  2. Robson Wolanski

    Hi Jeff! Thanks for this post. I was looking for something similar to protect JSON.
    I imagine to create an (or more) api key (somewhere inside wp-admin) and use this key in the endpoint, like:

    https://digwp.com/wp-json/wp/v2/users/“apikey”

    So, only the endpoint with the right api key will access the content through WP REST API.

    Do you know any plugin to handle this?

    Any security concerns using this “api key”?

    • Hi Robson, thank you for the comment. I like the idea of a key that could be used to access the REST API. That way, the admin could decide the ideal way to restrict RESTful content. My guess is that there already is a plugin that does something like this, although I have not searched as of yet. Either way, I’ve added this to the to-do list for the plugin, and will investigate further for the next update.

  3. It’s also a good reason why I created a “drop-in” solution for protecting the wp-login.php file: It requires the use of a valid email as the Login Name, all because THAT is not published in the extra info of any posts (at least, not with my current template anyway).

    Another point:

    I am seeing things like this in my access logs –

    POST /wp-json/ HTTP/1.1

    So, I used an htaccess trick to effectively block those kinds of attacks. Plus, my custom firewall application is also “keyed in” to detect and mark such attacks. (At one time, they even filled in a few fields with XML statements and properties – in an attempt to request other important info.)

    I just wish the WP crew left REST API in plugin form, rather than hard-code it into the WP core! – Not all of us use REST API anyway, it just becomes more “code bloat” to us.

    – Jim

    • Hey if it works for you then that’s all that matters. Personally I prefer to protect all REST activity using the Disable REST plugin, just a few lines of code and done. May be adding a couple more features but even then the plugin will be super lightweight (by design).

      And I 100% agree with you that features like REST should have been left a plugin. WordPress has developed a bad habit of completely ignoring its core principles (e.g., “clean, lean, and mean” et al).

  4. Good article. I’ve read of very popular plugin (contact form 7) breaking if you disable the REST API. Have you run into these concerns? Where disabling becomes a big hassle? Even Gutenberg relies heavily on the API, but I guess if you just block it for logged out users it would still work…..

    • I don’t know about Contact Form 7 (It’s way too bloated, so I wrote my own contact form plugin), and have not heard any concerns about other plugins. As explained in the article, if your plugins require REST API for the general public (non-logged-in visitors), then disabling REST is not recommended. And correct about Gutenberg, not an issue because you need to be logged in to use it.

  5. Thanks Jeff Starr for this great post keep it up

  6. I enjoy that all these blogs mention privacy issues… but nobody mentions that you can add an endpoint that edits the database, and it is available publicly by default.

    why not focus on how to set authorization privileges rather than just disabling the api?

    • The article discusses both privacy and security issues inherent in the REST API, but thanks for the comment. And of course, you are always welcome to write your own post on authorization privileges, or whatever you think is best.

Comments are closed for this post. Contact us with any critical information.
© 2009–2024 Digging Into WordPress Powered by WordPress Monzilla Media shapeSpace