Silver Bullets

Published 2022-07-29 in ci-cd
automation security wordpress

WordPress API authentication for automation POC

I last wrote about this in February 2022. I've since had more reason to leverage the WordPress API.

WordPress has integrated a REST API since version 4.7 (in 2016)

Their API is integrated, but the authentication is by cookie, only, by default. Unless you are using Selenium, or plugins, this makes integration testing very hard.

Literature review

If we can restrict ourselves to a predefined user and role, there is no need for the basic authentication plugin recommended by one site I came across (written in Dec 2021). The repo they linked, GitHub WordPress REST API group, hadn't been touched in 5 years and recommends OAuth plugin for production.

Basic authentication is more for convenience in a test environment than for authenticating against production servers. Sure it could serve in production, and if everything else was infallible, never get hacked, but it is suboptimal. In terms of security, a little complexity is a small price to pay for improvements, until optimal.

I sympathise for third parties attempting to document the state of WordPress. WordPress itself describes authentication mechanisms:

While cookie authentication is the only authentication mechanism available natively within WordPress

At least they aren't claiming to be up to date, that article is undated.

Application Passwords

When is a plugin not a plugin? since late 2020's WordPress 5.6

Application passwords is not a plugin, despite appearing in that section of WordPress's own authentication documentation. Until WP 5.6 it was a plugin. Then the plugin became core functionality. Previous literature about the plugin is still valid.

I'd recently seen GMail steer me toward application passwords. The account I use to send email from my monitoring script wasn't sending emails since the end of May 2022. Google wanted everyone to be using 2FA, but for automations that is not possible. Application passwords were the only viable alternative. I suppose I could have signed up with OAuth but that would have meant bothers.


  • Why is having another password less dangerous than Basic Authentication?
    • The other password has reduced scope to access the API only, so that if it becomes compromised the attacker will have fewer opportunities for mischief.
  • What is an example of something a password user could do that an application password could not?
    • Changing email addresses or passwords.
    • Generally, logging in via wp-login.php.
  • Apart from scoping, how is this any safer than basic authentication
    • By default, application passwords are only accepted over HTTPS
      • add_filter( 'wp_is_application_passwords_available', '__return_true' ); disables this feature.


Though on the cards, application passwords, at the time of writing (July 2022), only reduce privileges somewhat. Of course, we need to go further and leverage the WordPress roles I have hitherto overlooked.

For testing, a new user with the Contributor role will suffice for many things. It is like Author but can only create drafts. As the regression tests want to try posting images, this will not work, images are always published. Author is fine for now; they only have free rein over their own creations.

For the basic POC I will need a POST_ID to work on, so I create that draft manually.

Then using this more limited user, I will create, and record, the application and password:

APPLICATION_PASSWORD="alls here bees prnt able"


I create (name) an application password and record the 24 characters (spaces being optional) because it will not be recoverable later.

The script that follows is essentially the plugin's own getting started, in more detail, and my own words.

APPLICATION_PASSWORD="alls here bees prnt able"
curl --user "$USERNAME:$APPLICATION_PASSWORD" -X POST -d "title=New Title" https://localhost:$PORT_NUMBER/wp-json/wp/v2/posts/$POST_ID

I am immediately presented with an error pertaining to my self signed certificate. I am sure this is going to be very common with local testing.

I can add the infamous -k flag to curl, but that invalidates the test since end to end requires security. This is only local testing so we might prefer to do just that. It is worth understanding better solutions to this error for when we replace curl, with, say, Python.

My provisioning generates the self-signed certificate as (denoted by tls_cert_file) /etc/ssl/certs/apache-selfsigned.crt. I can either copy it directly or read a slightly more verbose version, remotely:

echo quit | openssl s_client -showcerts -servername "localhost" -connect localhost:$PORT_NUMBER > self-signed-cacert.crt

We can suffix it with crt or pem. It is both pem and a certificate.

Next we re-use those shell variables we defined earlier to vacillate between titles:

curl --cacert self-signed-cacert.crt --user "$USERNAME:$APPLICATION_PASSWORD" -X POST -d "title=New title" https://localhost:$PORT_NUMBER/wp-json/wp/v2/posts/$POST_ID
curl --cacert self-signed-cacert.crt --user "$USERNAME:$APPLICATION_PASSWORD" -X POST -d "title=No title" https://localhost:$PORT_NUMBER/wp-json/wp/v2/posts/$POST_ID

Next steps

With the POC done, the learning and exploitation continues. I have public git repos which not only wrap the basic REST API in python but have built an app on top of that to automate image posting and optimisation.

Log in to comment
Register to comment
0 Comments No comments yet...