Skip to content

CWE-472: External Control of Assumed-Immutable Web Parameter

Overview

Assumed-immutable parameter vulnerabilities occur when applications trust that client-controlled data (hidden form fields, cookies, disabled inputs, URL parameters) remains unchanged, failing to validate on server-side, enabling price manipulation, privilege escalation, and business logic bypass.

OWASP Classification

A06:2025 - Insecure Design

Risk

High: Trusting client-side "immutable" data enables price manipulation (hidden fields), privilege escalation (user role cookies), discount/coupon abuse, quantity limits bypass, shipping cost manipulation, and complete business logic compromise.

Remediation Steps

Core principle: Do not treat client-supplied parameters as immutable; recompute and verify server-side for every request.

Locate the Assumed-Immutable Parameter Vulnerability

When reviewing security scan results:

  • Examine data_paths: Identify where client-controlled data is trusted without server-side validation
  • Find assumed-immutable fields: Hidden form fields, disabled inputs, cookies, URL parameters
  • Trace parameter usage: Where are these values used - pricing, authorization, business logic
  • Check for validation: Look for missing server-side validation of "read-only" client data
  • Assess impact: Can users modify values to bypass limits, change prices, escalate privileges

Never Trust Client-Side Data (Primary Defense)

# WRONG - trusting hidden field
@app.route('/checkout', methods=['POST'])
def checkout():
    price = float(request.form['price'])  # User can modify!
    quantity = int(request.form['quantity'])
    total = price * quantity  # Manipulated price!

# RIGHT - lookup server-side
@app.route('/checkout', methods=['POST'])
def checkout():
    product_id = request.form['product_id']
    product = db.get_product(product_id)
    price = product.price  # Server-side source of truth
    quantity = int(request.form['quantity'])
    total = price * quantity

Why this works: All security-sensitive data must originate from and be validated on the server. Client-provided data can always be modified using browser dev tools, proxy tools, or direct HTTP requests.

Key principle: Use client data only as identifiers (IDs), never as trusted values (prices, roles, limits). Look up the actual values server-side based on the identifier.

Store Sensitive Data Server-Side Only

// WRONG - using cookies for authorization
// Cookie: role=admin  ← User can modify in browser
String role = request.getCookies()["role"];
if ("admin".equals(role)) { /* ... */ }

// RIGHT - use server-side session
HttpSession session = request.getSession();
String role = (String) session.getAttribute("role");

// BEST - lookup from database
User user = userService.getCurrentUser();
if (!user.hasRole("ADMIN")) {
    throw new AccessDeniedException();
}

What to store server-side:

  • User roles and permissions (in session or database)
  • Pricing information (in database, looked up by product ID)
  • Discount percentages (in user profile or promotion table)
  • Account balances and limits
  • Any data that affects authorization or business logic

Validate All Parameters Against Business Rules

# Even "read-only" fields must be validated
discount = float(request.form['discount'])

# Validate range
if discount < 0 or discount > 0.20:  # Max 20%
    raise ValueError("Invalid discount")

# Verify against user's actual entitlements
user = get_current_user()
if discount > user.max_allowed_discount:
    raise ValueError("Discount exceeds user limit")

# Validate quantity limits
quantity = int(request.form['quantity'])
if quantity < 1 or quantity > product.max_per_order:
    raise ValueError("Invalid quantity")

Validation rules:

  • Range checks (min/max values)
  • Business rule verification (user entitlements, order limits)
  • Data type validation
  • Cross-field validation (total = price × quantity)

Use Signed Tokens for Client State When Necessary

When you must pass sensitive data to client:

import hmac
import hashlib

SECRET_KEY = b'your-secret-key'  # From secure config

# Generate signed price token
def sign_price(price):
    signature = hmac.new(SECRET_KEY, str(price).encode(), hashlib.sha256).hexdigest()
    return f"{price}|{signature}"

# Verify on submission
def verify_price(token):
    try:
        price_str, signature = token.split('|')
        expected = hmac.new(SECRET_KEY, price_str.encode(), hashlib.sha256).hexdigest()
        if not hmac.compare_digest(signature, expected):
            raise ValueError("Tampered price detected")
        return float(price_str)
    except (ValueError, AttributeError):
        raise ValueError("Invalid price token")

When to use signed tokens:

  • Shopping cart state passed between pages
  • Temporary authorization grants
  • Workflow state in multi-step processes
  • Always prefer server-side sessions when possible

Monitor and Test for Parameter Tampering

Testing strategies:

  • Use browser dev tools to modify hidden fields before submission
  • Use proxy tools (Burp Suite, OWASP ZAP) to intercept and modify requests
  • Test modifying disabled form fields (enable with JavaScript, then change)
  • Test cookie modification: change role, permissions, user ID
  • Test URL parameter tampering: change prices, quantities, discounts
  • Test negative values, zero, extremely large values

Browser-based tests:

// In browser console - modify hidden field
document.querySelector('[name=price]').value = '0.01';

// Enable disabled field
document.querySelector('[name=discount]').disabled = false;
document.querySelector('[name=discount]').value = '100';

// Modify cookie
document.cookie = 'role=admin; path=/';

Monitoring:

  • Log parameter validation failures
  • Alert on suspicious patterns (prices set to $0.01, discount = 100%)
  • Track HMAC signature verification failures
  • Monitor for repeated tampering attempts from same user/IP
  • Review business metrics for anomalies (all orders $0.01)

Verification steps:

  • Modify all hidden fields and verify rejection
  • Change cookie values and verify they're ignored
  • Test with proxy tool to modify requests in flight

Common Vulnerable Patterns

<!-- Hidden field with price -->
<input type="hidden" name="price" value="99.99">
<input type="text" name="quantity">

<!-- Disabled field (can be enabled client-side) -->
<input type="text" name="discount" value="0" disabled>

<!-- Cookie with role -->
Set-Cookie: isAdmin=true

<!-- URL parameter -->
/purchase?item=123&price=99.99

Attack Examples

// Modify hidden field
document.querySelector('[name=price]').value = '0.01';

// Enable disabled field
document.querySelector('[name=discount]').disabled = false;
document.querySelector('[name=discount]').value = '100';

// Modify cookie
document.cookie = 'role=admin';

Additional Resources