Hi everyone. The REST API team recently discovered a bug with parameter parsing in the API infrastructure, part of WordPress 4.4. For those of you using the API infrastructure, you need to be aware of a bug fix we’re making with the API.
The REST API has several types of parameters that it mixes together. These come from several sources including the request body as either JSON or URL-encoded form data (
$_POST), query parameters (
$_GET), the API route, and internally-set defaults. Unfortunately, due to an oversight on our behalf, these parameters can be inconsistently formatted.
In WordPress, the superglobal request variables (
$_GET) are “slashed”; effectively, turning magic quotes on for everyone. This was originally built into PHP as a feature to help guard against SQL injection, but was later removed. Due to compatibility concerns, WP cannot change this behaviour for the superglobals. This only applies to the PHP superglobals, not to other sources of input like a JSON body or parameters in the URL. It additionally does not apply to form data on PUT or DELETE requests.
Internally, some low-level WordPress functions expect slashed data. These functions internally call
wp_unslash() on the data you pass in. This means input data from the superglobals can be passed in directly, but other data needs to be wrapped with a call to
When the REST API gathers the data sources, it accidentally mixes slashed and unslashed sources. This results in inconsistent behaviour of parameters based on their source. For example, data passed as a JSON body is unslashed, whereas data passed via form data in the body is slashed (for POST requests).
For example, the following two pieces of data are equivalent in the REST API:
// JSON body:
// Form-data ($_POST)
// Both result in:
$request->get_param('title') === 'Foo';
However, if the data contains slashes itself, this will be inconsistently passed to the callback:
// JSON body:
// Results in:
$request->get_param('title') === 'Foo\Bar';
// Form-data ($_POST) (%3D = "\")
// Results in:
$request->get_param('title') === 'Foo\\Bar';
This means that callbacks need to understand where parameters come from in order to consistently handle them internally. Specifically:
- Data passed in the query string (
$request->get_query_params()) is slashed
- Data passed in the body as form-encoded (
$request->get_body_params()) is slashed for
POST requests, and unslashed for
- Data passed in the body as JSON-encoded (
$request->get_json_params()) is unslashed.
- Data passed in the URL (
$request->get_url_params()) is unslashed.
- Data passed as a default (
$request->get_default_params()) is unslashed.
In addition, parameters set internally via
$request->set_param() are unslashed. Unit and integration tests for API endpoints typically use these directly, so the majority of tested code (such as the WP REST API plugin) assumes parameters are unslashed.
See the related Trac Ticket #36419 for more information.
The Solution for WordPress 4.4 and 4.5
We are regarding inconsistently-slashed data as a major bug, and are changing the API infrastructure to ensure unslashed data. This will ensure that data is consistent regardless of the source. Callbacks will now receive unslashed data only, and can rely on this regardless of the original data source or request method.
If you are using functions that expect slashed data in your callback, you will need to slash your data before passing into these functions. Commonly used functions that expect slashed data are
wp_insert_user, along with others. Before passing data into these functions, you must call
wp_slash() on your data.
The fix for this issue, will be included in the WordPress 4.5 release candidates and final release. Due to the severity of the bug, we are also backporting the fix to the next minor WordPress 4.4 update. This also ensures you can update your plugins can act consistently across all versions of the REST API.
We understand that this may inadvertently break some plugins that are expecting slashed data. Right now, it’s not possible to consistently ensure that callbacks receive slashed data, so it is likely that these plugins will already break in some conditions.
tl;dr: if you’re using
*_post_meta in your REST API callback, you need to ensure you are calling
wp_slash() on data you are passing in, regardless of source.
We apologize for this bug existing in the first place. Slashed data is a problem that has plagued WordPress for a long time, and we’re not immune to getting caught by the issue ourselves.