Hidden Sensitive Fields
ID |
html.hidden_sensitive_fields |
Severity |
high |
Resource |
Access Control |
Language |
Html |
Tags |
CWE:807, NIST.SP.800-53, OWASP:2021:A4, PCI-DSS:6.5.4, PCI-DSS:6.5.6 |
Description
This rule identifies HTML hidden input fields (type="hidden") that contain security-relevant or business-critical data such as user roles, permissions, pricing information, or authentication tokens. Hidden fields are trivially accessible and modifiable by users through browser developer tools, making them unsuitable for storing any data that impacts security decisions or business logic.
Rationale
Hidden input fields exist purely in the HTML source code and provide no security whatsoever. They are a common vulnerability vector due to several critical weaknesses:
Trivial Manipulation: Any user can open browser DevTools (F12), locate hidden fields in the DOM inspector, and modify their values before form submission. No technical expertise is required.
Authorization Bypass: Storing user roles, permission levels, or account types in hidden fields allows privilege escalation attacks. An attacker can change role=user to role=admin and gain unauthorized access.
Price Tampering: E-commerce applications that store prices, discounts, or total amounts in hidden fields are vulnerable to financial fraud. Users can modify price=100.00 to price=1.00 before checkout.
Session Manipulation: Hidden fields containing session identifiers, user IDs, or authentication tokens can be stolen via cross-site scripting (XSS) or modified to impersonate other users.
Client-Side Security Theater: Relying on hidden fields for security creates a false sense of protection while offering none. It violates the fundamental principle that client-side data is inherently untrusted.
Compliance Violations: Standards like PCI-DSS prohibit storing sensitive authentication data and require server-side validation of all security-relevant decisions. Hidden fields for authorization or pricing violate these requirements.
Consider the following code:
<form method="POST" action="/checkout">
<input type="hidden" name="user_role" value="customer" />
<input type="hidden" name="price" value="99.99" />
<input type="hidden" name="discount" value="0" />
<input type="hidden" name="is_admin" value="false" />
<input type="text" name="quantity" />
<button type="submit">Purchase</button>
</form>
An attacker using browser DevTools can easily modify these values:
// In browser console:
document.querySelector('[name="user_role"]').value = 'admin';
document.querySelector('[name="price"]').value = '0.01';
document.querySelector('[name="discount"]').value = '100';
document.querySelector('[name="is_admin"]').value = 'true';
Remediation
Never store security-relevant or business-critical data in hidden fields. Always maintain authoritative state on the server side and validate all decisions server-side based on trusted session data.
Vulnerable Pattern:
<form method="POST" action="/process-order">
<input type="hidden" name="user_id" value="12345" />
<input type="hidden" name="is_premium" value="false" />
<input type="hidden" name="total_price" value="199.99" />
<input type="hidden" name="role" value="user" />
<input type="text" name="shipping_address" />
<button type="submit">Complete Order</button>
</form>
Secure Pattern:
<form method="POST" action="/process-order">
<!-- Only store non-sensitive identifiers or CSRF tokens -->
<input type="hidden" name="csrf_token" value="a8f7d9e6c4b2..." />
<input type="hidden" name="form_id" value="checkout_form" />
<input type="text" name="shipping_address" />
<button type="submit">Complete Order</button>
</form>
Server-side implementation (secure):
@app.route('/process-order', methods=['POST'])
def process_order():
# Get user from authenticated session (server-side)
user = get_authenticated_user(session)
# Calculate price server-side based on session data
cart = get_user_cart(user.id)
total_price = calculate_total(cart, user.is_premium)
# Verify authorization server-side
if not user.has_permission('place_order'):
return error(403, "Unauthorized")
# Process order with server-calculated values
process_payment(user, total_price)
Acceptable Uses of Hidden Fields (non-security data only):
-
CSRF tokens (properly generated and validated server-side)
-
Form workflow state (e.g., "step=2" in multi-step forms)
-
Non-sensitive reference IDs that are validated server-side
-
Analytics or tracking parameters
Key Security Principles:
-
Never trust client-side data - treat all form data as hostile input
-
Server-side authority - all security and pricing decisions must be made server-side
-
Session-based state - use server-side sessions for user identity and permissions
-
Cryptographic validation - if hidden fields are necessary, use HMAC or digital signatures to detect tampering
-
Defense in depth - assume hidden fields will be modified and validate accordingly
Configuration
This detector has no configurable properties and checks all <input type="hidden"> fields by default for security-relevant patterns, including:
-
Authorization data: role, permission, is_admin, user_type, access_level
-
Pricing data: price, total, amount, cost, discount, fee
-
User identity: user_id, account_id, customer_id
-
Authentication: token (non-CSRF), session_id, auth_key
-
Business logic: is_premium, subscription_tier, credit_balance