Make WordPress Core

Opened 3 years ago

#53994 new defect (bug)

REST API requests with session cookies but an invalid/missing nonce are considered authenticated for most of the request

Reported by: mboynes's profile mboynes Owned by:
Milestone: Awaiting Review Priority: normal
Severity: normal Version:
Component: Security Keywords:
Focuses: rest-api Cc:

Description

Note: the security team reviewed this and asked that I post a public report as a hardening issue.

If a REST API request has valid session cookies but does not include a nonce, there is a significant portion of the process where the user is considered validly authenticated and signed in until the nonce is finally checked and the user account is nulled out. This period of the request is, practically speaking, the entire portion of the request where a plugin or theme would perform any operations before the REST response was sent (the obvious exception is if the REST endpoint were a custom one created by the theme or plugin, in which case, any code in direct service of dispatching the response would be after the nonce was checked). Therefore, if a request contains valid session cookies but an invalid or absent nonce, plugins and themes will almost always treat the request as if the user were validly authenticated, potentially voiding the purpose of the nonces.

In WordPress Core, the first place the user account is accessed is in \WP::init(). After that, the init action fires, which of course, is a common hook for plugins and themes. In addition, there is nothing stopping a plugin or theme from accessing the user account well before \WP::init(), e.g. immediately as it is loaded. In other words, a user session is validated as early as an mu-plugin loading. Out-of-the-box, WordPress does make some decisions based on the user's logged-in status and capabilities, e.g. which mime types are allowed for an attachments REST request and how widget content is processed. In fact, even the REST API code itself decides whether or not to send nocache headers based on the user's authentication state before the nonce is checked.

The request isn't de-authenticated again until the nonce is checked when
\WP_REST_Server::check_authentication() runs, which is immediately before the response is dispatched in \WP_REST_Server::serve_request().

Steps To Reproduce:

  1. Sign into a WordPress site
  2. Add the following code to your theme/plugin to confirm that you are considered "signed in", even though you didn't provide a nonce
    <?php
    add_action( 'init', function() { die( is_user_logged_in() ? 'You are signed in' : 'You are not signed in' ); } );
    
  3. Make a REST API request from the same browser, without a nonce present

Recommendations

My best suggestion for resolving the issue is to check the nonce as part of the determine_current_user filter. However, this would require that WordPress make some assumptions about the request and infer that it is a REST request before that decision is officially made, since at this point, the REST_REQUEST constant might not have been defined. Potentially, that makes #42061 a pre-requisite.

Impact

I don't believe this to be a significant security vulnerability; after all, the user would still have to be validly authenticated. But it is certainly a vector for a CSRF attack, as it can potentially circumvent nonces. As far as I can tell, nothing in WordPress core is critically impacted out-of-the-box. The real impact I can foresee is that a plugin or theme performs an action for authenticated users during the request chain, and that action is exploited in a CSRF attack.

Change History (0)

Note: See TracTickets for help on using tickets.