The modern web is a complex beast. We build intricate user interfaces designed to guide users, validate their inputs, and ensure they follow the rules. But sometimes, in the rush to build a seamless experience, developers forget the golden rule of cybersecurity: Never trust the client.
Recently, while hunting on a major grocery delivery platform (let's call it redacted.com), I stumbled upon a classic example of this oversight. It was a logic flaw that allowed me to completely bypass their business rules regarding minimum order values and item quantity limits, straight through their GraphQL API.
Here's how a simple interception turned rigid business constraints into mere suggestions.
The Setup
I was browsing the storefront of a popular retailer hosted on redacted.com. Like most e-commerce giants, they have rules to ensure profitability and manage inventory.
I noticed two specific constraints while building a test cart:
- Quantity Limits: For certain popular items (like avocados, in this case), the UI explicitly stopped me from adding more than 10 units.
2. Minimum Order Value: The checkout button remained stubbornly grayed out until my cart total hit at least $10.00.
These are standard business practices. The UI was doing its job perfectly — preventing me, the average user, from breaking the rules via the browser interface.
But as a bug hunter, my first question is always: Is this rule enforced by the browser, or the server? I decided to find out.
The Hunt
I loaded my cart with 10 avocados. At $0.99 each, my total was $9.90 — just ten cents shy of the checkout minimum. The UI had me locked down.
This is where things got interesting. I fired up my proxy tool to peek under the hood. When I adjusted items in my cart, the application fired off a GraphQL request. I located the specific operation responsible for this: UpdateCartItemsMutation.
It was sending a clean JSON payload to the backend telling it exactly what quantity I wanted. The browser thought the limit was 10. I wondered if the server agreed.
I intercepted the request. Instead of letting the browser send the approved quantity of 10, I modified the payload to demand 15 units.
Technical Breakdown
The vulnerability lay in the fact that the backend GraphQL resolver implicitly trusted whatever integer I sent in the quantity field, without cross-referencing the product's actual constraints database.
Here is a simplified view of the attack.
Target Endpoint: POST https://redacted.com/graphql
The Intercepted Payload:
I took the legitimate request and just nudged the number.
JSON
{
"operationName": "UpdateCartItemsMutation",
"variables": {
"cartItemUpdates": [
{
// The ID for the avocado product
"itemId": "items_xxxx-yyyy",
// The UI limit was 10. I changed this to 15.
"quantity": 15,
"quantityType": "each"
}
],
"cartType": "grocery",
"cartId": "123456789"
}
// ... rest of the request
}I sent the modified request on its way. I was expecting a 400 Bad Request or a GraphQL error telling me I'd exceeded the limit.
Instead, the server responded with a 200 OK and a JSON object confirming the cart update with 15 items. I refreshed the browser, and sure enough, my cart was now holding 15 avocados, completely ignoring the UI limit.
Bypassing the Minimum Value
Having proven the server was gullible on quantities, I tested the second rule: the $10 minimum.
I repeated the interception, but this time I set the quantity to just 5 units, dropping my total cart value to $4.95.
JSON
"quantity": 5Again, the server accepted the update without complaint. When I returned to the UI, the previously grayed-out "Checkout" button was now active, ready to process an order well below the mandatory minimum.
Let's Hunt ! > Satyam > 🔗 Follow me on X