Parameter Tampering — Price Manipulation
Parameter tampering is a type of web application/API security issue where an attacker modifies a parameter controlled by the client and the server incorrectly trusts the modified value.
The important concept is:
SECURITY_ADVISORYThe client sends the input; the server must decide whether that input is actually allowed.
If the server accepts a manipulated parameter without properly validating it or checking authorization/business rules, the application may be vulnerable.
1. What Is a Parameter?
A parameter is information sent from the client to the server.
For example:
GET /account?user_id=1001Here:
user_id=1001is a parameter.
A POST request could contain:
POST /purchase
product_id=500
quantity=2
price=100An API might use JSON:
{
"product_id": 500,
"quantity": 2,
"price": 100
}All of these values can potentially be controlled or modified by the client.
2. What Does Tampering Mean?
Suppose an application sends:
POST /purchase
product_id=500&quantity=2&price=100The attacker changes:
price=100to:
price=1and sends:
POST /purchase
product_id=500&quantity=2&price=1If the server processes the purchase for $1 instead of obtaining the legitimate price from its database, the application has a business-logic parameter tampering vulnerability.
The fundamental problem isn't that the attacker changed the request. Clients are inherently capable of changing their own requests.
The problem is that the server trusted a value that should have been controlled by the server.
3. Common Types of Parameter Tampering
A. Price Manipulation
An application sends:
{
"product_id": 123,
"price": 9999
}The legitimate price is stored on the server as ₹9,999.
If changing the request to:
{
"product_id": 123,
"price": 10
}causes the transaction to be processed for ₹10, that's a serious business-logic flaw.
Proper Implementation
The server should essentially do:
Receive product_id
↓
Look up product_id in database
↓
Retrieve trusted price
↓
Calculate total
↓
Process paymentrather than:
Receive product_id + price
↓
Trust client-supplied price
↓
Process paymentThink of it this way:
Secure Application
Suppose the database contains:
Product ID: 101
Price: ₹5,000The browser sends:
POST /buy
product_id=101&price=10A secure server says:
Client says price = ₹10
↓
Server ignores that price
↓
Looks up product_id=101
↓
Database says ₹5,000
↓
Uses ₹5,000So changing the parameter has no useful effect.
Vulnerable Application
The server might have the correct price in the database, but the developer has written the business logic something like:
Receive product_id
Receive price
↓
Calculate using received price
↓
Process transactionThen:
product_id=101&price=10could result in:
Order total = ₹10The fact that the server has the correct value doesn't automatically mean it will use it.
4. Quantity Manipulation
Suppose:
POST /order
product_id=123&quantity=1The application should enforce rules such as:
quantity >= 1
quantity <= available_stockIf the application accepts something unexpected such as:
quantity=-1or an excessively large quantity, the result can depend on the application's business logic.
Potential consequences include:
- ›Incorrect inventory calculations
- ›Incorrect billing
- ›Negative totals
- ›Order-processing errors
- ›Resource exhaustion
The exact impact depends on how the backend handles the value.
5. How to Fix / Remediation
The client can send a price, but the server should ignore it and calculate the price from trusted server-side data.
Vulnerable Flow
Client
|
| product_id=101
| price=₹10 ← attacker modified this
| quantity=1
↓
Server
|
| trusts price=₹10
↓
Order = ₹10Even if the database contains:
Product 101 → ₹50,000the vulnerable code never uses that value.
Secure Flow
Client
|
| product_id=101
| price=₹10 ← ignored
| quantity=1
↓
Server
|
| 1. Validate product_id & quantity
↓
| 2. Authenticate user
↓
| 3. Check authorization
↓
| 4. Get product_id=101 from database
↓
Database
|
| Product 101 → ₹50,000
↓
Server
|
| 5. price = database.price
| 6. total = ₹50,000 × quantity
↓
Payment / Order
|
↓
₹50,000Vulnerable Code
product_id = request.json["product_id"]
price = request.json["price"]
quantity = request.json["quantity"]
total = price * quantityThe problem is:
price = request.json["price"]because request data is controlled by the client.
Fixed Code
product_id = request.json["product_id"]
quantity = request.json["quantity"]
product = get_product_from_database(product_id)
if not product:
return {"error": "Product not found"}, 404
price = product.price
total = price * quantityNow:
Client says: price = ₹10
↓
IGNORE
↓
Database says: price = ₹50,000
↓
Server uses: ₹50,000For a Real Payment System, Go One Step Further
Don't trust the client for any financial calculation:
- ›❌ Price
- ›❌ Discount
- ›❌ Tax
- ›❌ Shipping fee
- ›❌ Final amount
- ›❌ Currency
Instead:
product_id
quantity
↓
Server
↓
Fetch product price
↓
Validate quantity/stock
↓
Calculate subtotal
↓
Calculate permitted discount
↓
Calculate tax/shipping
↓
Calculate final amount
↓
Create payment/orderAlso make sure the payment provider amount is generated from this server-side calculation, rather than accepting a total amount supplied by the browser.