The Ravens store sends the item price in the purchase request from the browser, and the server charges the price that arrives instead of looking it up, so you pay whatever you want.
A purchase page in the Ravens store. The item costs 500 feathers and the price is sent inside the request. The server believes the incoming price, so you can edit it in the inspector to pay less.
Buy the item for less than its real price.
You bought it for less than the real price.
Why the solution worked
The server trusted a client-sent value for a financial decision. The price must be looked up by the server from its catalogue, not accepted from the request. Every client value is untrusted.
The root cause
The server charges the price in the request instead of looking it up. The root is trusting client data for money.
How the code is fixed
Never trust a client price; look it up on the server by item id.
Vulnerable code
app.post('/checkout', (req, res) => {
// Charges the price the client sent.
charge(req.body.price);
});
Safe code
app.post('/checkout', (req, res) => {
// Look the price up on the server by item id; ignore any client price.
const price = catalogue.get(req.body.item).price;
charge(price);
});
Impact in the real world
Price tampering allows near-free purchases and has been abused in real stores to order goods at tiny or negative prices.
How defenders detect it
Defenders always compute the price on the server and alert on purchase requests whose price does not match the catalogue.