WooCommerce is Exposing Private Product Information Through Store API
While looking into something related to the now discontinued WooCommerce Blocks plugin from Automattic, we noticed what appeared to be a vulnerability in that. That plugin has long been incorporated into the main WooCommerce plugin and we confirmed the vulnerability exists in the latest version of that plugin. The vulnerability exposes information that isn’t meant to be public about WooCommerce products through the WooCommerce Store API. There are possibly more issues related to that API, as we have only looked into this specific issue so far.
According to the Store API Guiding principles, private data shouldn’t be provided through the API (emphasis theirs): “Store data such as settings (for example, store currency) is permitted in responses, but private or sensitive data must be avoided.” Despite that statement, it doesn’t appear that some basic security reviewing has been done on the code. And it hasn’t been done in years, as the vulnerable code dates back four years. More thoroughly reviewing that needs to be done by Automattic.
That security isn’t being handled well with WooCommerce isn’t exactly an unknown issue. It also isn’t a great sign that “The Definitive Guide to WooCommerce Security” on the WooCommerce website doesn’t address how the developers at Automattic are supposed to be avoiding this type of situation, but instead is largely marketing material for another Automattic solution, Jetpack.
The vulnerable code exists in the file /src/StoreApi/Routes/V1/ProductsById.php. In the file, the function get_route_response() will return information about a product specified by an ID without checking if there are any restrictions on it being shown publicly:
78 79 80 81 82 83 84 85 86 | protected function get_route_response( \WP_REST_Request $request ) { $object = wc_get_product( (int) $request['id'] ); if ( ! $object || 0 === $object->get_id() ) { throw new RouteException( 'woocommerce_rest_product_invalid_id', __( 'Invalid product ID.', 'woocommerce' ), 404 ); } return rest_ensure_response( $this->schema->get_item_response( $object ) ); } |
As can be seen with the proof of concept below, when getting information on all the products through the Store API instead of one product based on the ID, it will correctly check for such restrictions and will not display products that have their visibility set to private or are still drafts.
As IDs are simply integers, all private/draft products can be viewed by iterating through all possible values.
The vulnerable portion of the API was highlighted when the Store API was labeled as being “stable” in March 2022.
WordPress Causes Full Disclosure
As a protest of the moderators of the WordPress Support Forum’s continued inappropriate behavior we changed from reasonably disclosing to full disclosing vulnerabilities for plugins in the WordPress Plugin Directory in protest, until WordPress gets that situation cleaned up, so we are releasing this post and then leaving a message about that for the developer through the WordPress Support Forum. (For plugins that are also in the ClassicPress Plugin Directory, we will follow our reasonable disclosure policy.)
You can notify the developer of this issue on the forum as well.
After four years, the moderators have finally tacitly admitted they were behaving inappropriately and have made moves to fix the problems (though incompletely), so these full disclosures can be ended if they simply restore access to our accounts and plugins in the Plugin Directory. Hopefully that takes less than four years.
Is It Fixed?
If you are reading this post down the road the best way to find out if this vulnerability or other WordPress plugin vulnerabilities in plugins you use have been fixed is to sign up for our service, since what we uniquely do when it comes to that type of data is to test to see if vulnerabilities have really been fixed. Relying on the developer’s information can lead you astray, as we often find that they believe they have fixed vulnerabilities, but have failed to do that.
Proof of Concept
Create a WooCommerce product that is published, and the visibility is set to public.
When visiting the following Store API URL, the product will be shown when not logged in to WordPress.
Replace “[path to WordPress]” with the location of WordPress.
http://[path to WordPress]/wp-json/wc/store/v1/products
With the Store API URL for getting a product by ID, it will also be shown when not logged in to WordPress.
Replace “[WooCommerce product ID]” with the ID of the product.
http://[path to WordPress]/wp-json/wc/store/v1/products/[WooCommerce product ID]
Now set the product to be private. It will no longer be shown with the first URL, but will continue to be shown with the second.