WooCommerce guest order enumeration: product catalog and order exposure

WooCommerce's REST API can expose order data and product catalogs without authentication. Guest checkout orders are especially easy to enumerate.

WooCommerce guest order enumeration — REST endpoints leaking order and customer data

WooCommerce ships with a REST API enabled by default. Certain endpoints are unauthenticated by default or can leak data through predictable order IDs and lax permission checks. Guest checkout orders — placed without a user account — are especially vulnerable to enumeration.

WooCommerce REST API endpoints that expose data

The WooCommerce REST API is at /wp-json/wc/v3/. Key endpoints to check:

  • /wp-json/wc/v3/products — full product catalog including hidden products, if authentication isn't enforced.
  • /wp-json/wc/v3/products/categories — all product categories including draft/hidden ones.
  • /wp-json/wc/v3/orders — order list (requires authentication by default, but misconfiguration or plugin interference can open this).
  • /wp-json/wc/v3/orders/{id} — individual order by ID. IDs are sequential integers.

Test unauthenticated access:

curl -s https://yourdomain.com/wp-json/wc/v3/products | python3 -m json.tool | head -50

A JSON response with product data — without any API key in the request — indicates unauthenticated product catalog access.

Guest checkout order enumeration

WooCommerce order IDs are sequential. An order at /wp-json/wc/v3/orders/1001 means there are likely orders at 1002, 1003, and so on. For guest checkout orders, WooCommerce includes a guest order view mechanism via a unique key, but the order key is often guessable or leaked through order confirmation emails with predictable URL structures.

The standard guest order view URL format:

https://yourdomain.com/checkout/order-received/{order-id}/?key=wc_order_{key}

The key is a 13-character alphanumeric string. If an attacker obtains one valid key (e.g., from a phishing email capture or search engine cache), they can use it to access that order's details: billing name, address, email, purchased items, and shipping details.

Product catalog exposure implications

Even if order data is protected, an exposed product catalog can reveal:

  • Pre-launch products — draft products set to hidden visibility may still appear in REST API responses if visibility filters aren't applied to the API context.
  • Cost/margin data — if you store cost-of-goods metadata (common with inventory plugins), this can appear in API responses.
  • Inventory levels — stock quantities enable competitors to track your inventory and time promotions.
  • Private product lines — wholesale-only or gated products may be visible in the catalog endpoint even when they're hidden from the storefront.

How to identify exposure

Test the products endpoint without credentials:

curl -s -o /dev/null -w '%{http_code}' https://yourdomain.com/wp-json/wc/v3/products

A 200 response means the catalog is publicly accessible. A 401 means authentication is required (correct behavior).

Test order endpoint:

curl -s -o /dev/null -w '%{http_code}' https://yourdomain.com/wp-json/wc/v3/orders

Test a specific order ID (guess a plausible recent order number):

curl -s https://yourdomain.com/wp-json/wc/v3/orders/1001

Remediation

Require authentication for all WooCommerce API endpoints

In WooCommerce settings: WooCommerce → Settings → Advanced → REST API. Revoke any API keys with broader than necessary permissions. Create per-integration API keys with minimum required scopes.

Enforce authentication via filter:

add_filter('woocommerce_rest_check_permissions', function($permission, $context, $object_id, $post_type) {
    if (!is_user_logged_in()) {
        return false;
    }
    return $permission;
}, 10, 4);

Restrict product catalog visibility

For products you don't want in the API: set the catalog visibility to Hidden in the product edit page. Also ensure catalog_visibility is respected in API queries — some plugins override this.

API key rotation

If you suspect existing API keys have been exposed (e.g., in git history, error logs, or environment files), rotate them immediately: WooCommerce → Settings → Advanced → REST API → Revoke. Generate new keys with the minimum required scope (Read, Write, or both, per endpoint).

Endpoint restriction at web server level

# Nginx: block unauthenticated WC API access
location ~ ^/wp-json/wc/ {
    # Require presence of Authorization header
    if ($http_authorization = '') {
        return 401;
    }
}

Note: this blocks all unauthenticated WC API requests, including from the storefront JS. Verify that your checkout flow doesn't use the WC REST API without credentials before applying this.

Common mistakes

  • Creating API keys with full Read/Write scope for read-only integrations — use Read scope for analytics/sync plugins that don't need to write orders.
  • Committing API keys to git — use environment variables. Scan your history with git log --all -S 'ck_' (WooCommerce consumer keys start with ck_).
  • Not testing after applying restrictions — verify with curl that the endpoints return 401 before considering the fix complete.
  • Ignoring the product endpoint when order endpoint is locked — separate concerns, both require review.

Orb44's outside scan checks WooCommerce REST API endpoints for unauthenticated access, including the products and orders endpoints. Check your store at orb44.com.

FAQ

Are WooCommerce order IDs truly sequential and predictable?

Yes. WooCommerce uses WordPress post IDs for orders, which are a global sequential integer across all post types. An order ID of 5000 tells an attacker that other orders exist in nearby ranges. The guest order key provides some protection, but the ID itself is always predictable.

Do I need the WooCommerce REST API enabled if I'm not using integrations?

The WooCommerce REST API is required by the admin dashboard's block-based order management, some payment gateways, and many popular plugins. Disabling it entirely is rarely practical. The correct approach is restricting which endpoints are publicly accessible and ensuring API keys are scoped minimally.

Orb44 Journal · all posts