1

I've been tasked with managing a WordPress site that was developed and configured by some other team. During a basic security scan, I've realized that the following end-point https://www.mywordpress.com/wp-json does not restrict the Origin for some reason.

curl -H "Origin: https://www.thewrongorigin.com" -I https://www.mywordpress.com/wp-json
HTTP/2 200
server: nginx
date: Mon, 22 Mar 2021 11:02:54 GMT
content-type: application/json; charset=UTF-8
vary: Accept-Encoding
access-control-expose-headers: X-WP-Total, X-WP-TotalPages, Link
access-control-allow-headers: Authorization, X-WP-Nonce, Content-Disposition, Content-MD5, Content-Type
allow: GET
access-control-allow-origin: https://www.thewrongorigin.com
access-control-allow-methods: OPTIONS, GET, POST, PUT, PATCH, DELETE
access-control-allow-credentials: true

… other headers here…

Looking at the response, and using different URLs as origin, it looks like that my WordPress installation is setting the header access-control-allow-origin to whatever the origin of the request is. I was expecting a response with the following header:

curl -H "Origin: https://www.thewrongorigin.com" -I https://www.mywordpress.com/wp-json
HTTP/2 200
...
access-control-allow-origin: https://www.mywordpress.com

What I want to do is modify my WordPress and set the header Access-Control-Allow-Origin: https://www.mywordpress.com for the URL https://www.mywordpress.com/wp-json.

My questions are:

  • Is it okay to set restrict the origin in this way?
  • What might be the consequences of doing it? Maybe plugins that rely on a header like access-control-allow-origin: * could stop working? Are there such plugins?

I've experience coding and working as a system administrator, but I'm fairly new to WordPress.

2 Answers 2

3

First of all, this is intentional behaviour, as relayed in a Slack discussion described in this ticket (this has likely been discussed in other places, but that's the first I found):

tl;dr: CORS is built for CSRF protection, but WordPress already has a system for that (nonces), so we "disable" CORS as it gets in the way of alternative authentication schemes

...

it's a design decision to expose data from the REST API to all origins; you should be able to override in plugins easily

So it should be noted that it's perfectly normal for wp-json/ to be accessible from all origins, and it's not inherently insecure.

As for your questions:

Is it okay to set restrict the origin in this way?

By modifying your WordPress install itself? Absolutely not. You should never edit WordPress core files. Any changes you make will be overwritten any time WordPress updates, forcing you to make the change again every time, possibly leading you into the very bad practice of maintaining your own fork of WordPress.

If you want to modify core WordPress behaviour yourself, you need to create a plugin, and use the Hooks API to make your changes by removing and replacing actions, or filtering values.

In this case, you'll notice that the header is sent in the rest_send_cors_headers() function. This function is run by WordPress by hooking it with this line:

add_filter( 'rest_pre_serve_request', 'rest_send_cors_headers' );

Which, according to inline documentation is inside a function that is:

Attached to the {@see 'rest_api_init'} action to make testing and disabling these filters easier.

So, if you want to modify rest_send_cors_headers() what you need to to is create a plugin, and inside that plugin copy the rest_send_cors_headers() function to a new function with a different name. Then you want to replace WordPress' version with yours:

add_action(
    'rest_api_init',
    function() {
        remove_filter( 'rest_pre_serve_request', 'rest_send_cors_headers' );
        add_filter( 'rest_pre_serve_request', 'my_new_rest_send_cors_headers' );
    }
);

Where my_new_rest_send_cors_headers() is the name of your new modified version of the function.

What might be the consequences of doing it? Maybe plugins that rely on a header like access-control-allow-origin: * could stop working? Are there such plugins?

Most plugins are unlikely to have issues, as they will be making the requests from the same origin, however you will have issues with two types of plugins that I can think of:

  1. Plugins that work with an external service. That service may want to connect to your website via the REST API, and will be unable to do so if you are only allowing requests from one origin.
  2. Plugins that make requests to your site's REST API from the server, using curl, or the WordPress HTTP API, without setting the Origin header to your site's URL. This is pretty unusual and rare, but not impossible.

That last point brings up an important issue: Setting Access-Control-Allow-Origin is not going to protect your data. It is trivial for a server to spoof the origin and make a request to the API and get that data. This header is not an authentication security measure, so if you want to lock down your API from the outside world, this header is not the way to do it.

6

FYI, CORS is not for CSRF protection. Setting a CORS policy is selectively removing security controls. You're essentially saying some other site, as indicated in the Access-Control-Allow-Origin, is allowed to interact with your WordPress site.

This can get dangerous if you have Access-Control-Allow-Credentials set to true.

This will tell the user's browser to include any session cookies in cross origin requests.

Let's say WordPress automatically sets the CORS policy based on a requests Origin (some URLs will do that in WordPress). I'm making JavaScript requests from maliciouswebsite.com in the background to somewordpresssite.com. The JavaScript request made from maliciouswebsite.com sets an origin automatically to maliciouswebsite.com. Somewordpressite.com responds with a CORS policy of:

Access-Control-Allow-Origin: https://maliciouswebsite.com
Access-Control-Allow-Credentials: true

Great. Now I trick a user logged into somewordpresssite.com to visit maliciouswebsite.com, and while they're surfing my malicious page, I can have JavaScript making requests to the URLs on somewordpresssite.com that uses the CORS policy. And the victim's web browser will happily attach their session to those JavaScript request, so I'm access the wordpress site as the victim.

My malicious JavaScript can read pages with the victim's session, and make requests to those pages with the CORS policy. I can easily read the CSRF tokens myself, and resend those with requests as needed. It's fairly easy to bypass CSRF controls when you have read access with JavaScript. Plenty of examples of that for WordPress here: https://github.com/hoodoer/WP-XSS-Admin-Funcs/blob/943a34c29524c4cdf02bdb1f20d5e6fe6d1cc487/wpXssAdminFuncs.js

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.