Weakness reference
CWE-807

Reliance on Untrusted Inputs in a Security Decision

This weakness occurs when an application makes a security-critical decision—such as granting access, approving a transaction, or determining user…

01Summary

This weakness occurs when an application makes a security-critical decision—such as granting access, approving a transaction, or determining user permissions—based on data that comes from an untrusted source without proper validation. An attacker can manipulate that input to bypass security controls, gain unauthorized access, or perform actions they shouldn't be allowed to perform.

02How It Happens

Security decisions should be based on authoritative, server-controlled data: session tokens, cryptographic signatures, or database records that the application itself manages. Instead, this weakness arises when developers trust client-supplied data—URL parameters, form fields, cookies, headers, or hidden form values—to enforce access control. Even if the data is encrypted or obfuscated, if the client can modify it and the server doesn't re-validate it against a trusted source, an attacker can forge or alter it to change the outcome of the security decision.

The root cause is often a misunderstanding of the trust boundary: the assumption that because data came from "the user's browser," it must be legitimate. In reality, an attacker controls their own browser and can send any data they want.

03Real-World Impact

Exploitation can lead to unauthorized access to accounts, elevation of privileges, bypassing payment or approval workflows, or accessing data belonging to other users. For example, an attacker might modify a user ID in a request to view another user's profile, or change a role parameter to grant themselves admin privileges. The severity depends on what decision is being made—a flawed authorization check can expose sensitive data or allow account takeover.

04Vulnerable & Fixed Patterns

Vulnerable pattern
from flask import Flask, request

app = Flask(__name__)

@app.route('/user_profile')
def user_profile():
    # Attacker can modify 'user_id' in the query string
    user_id = request.args.get('user_id')
    
    # Security decision based entirely on untrusted input
    if user_id == '42':
        return "Admin panel access granted"
    else:
        return "User profile for ID: " + user_id

Why it's vulnerable:
The user_id comes directly from the URL query string. An attacker can change it to any value, and the application will trust it to make an access-control decision without verifying it against the logged-in user's actual identity or permissions stored on the server.

Fixed pattern
from flask import Flask, request, session

app = Flask(__name__)
app.secret_key = 'your-secret-key'

@app.route('/user_profile')
def user_profile():
    # Get the authenticated user from the server-side session
    authenticated_user_id = session.get('user_id')
    
    if not authenticated_user_id:
        return "Unauthorized", 401
    
    # Optionally accept a user_id parameter, but only if it matches the authenticated user
    requested_user_id = request.args.get('user_id')
    
    if requested_user_id and requested_user_id != authenticated_user_id:
        return "Forbidden", 403
    
    return f"User profile for ID: {authenticated_user_id}"
Vulnerable pattern
<?php
// Attacker can modify 'role' in the form or URL
$role = $_GET['role'] ?? 'user';

// Security decision based on untrusted input
if ($role === 'admin') {
    echo "Admin dashboard loaded";
    // Grant admin access
} else {
    echo "User dashboard loaded";
}
?>

Why it's vulnerable:
The role parameter comes directly from user input. An attacker can simply add ?role=admin to the URL and the application will treat them as an administrator without verifying their actual permissions against the database or session.

Fixed pattern
<?php
session_start();

// Verify the user is logged in
if (empty($_SESSION['user_id'])) {
    http_response_code(401);
    die("Unauthorized");
}

// Fetch the user's actual role from the database (trusted source)
$user_id = $_SESSION['user_id'];
$role = get_user_role_from_database($user_id);

// Make the security decision based on the server-side role
if ($role === 'admin') {
    echo "Admin dashboard loaded";
} else {
    echo "User dashboard loaded";
}

function get_user_role_from_database($user_id) {
    // Query your database to retrieve the user's actual role
    // (using prepared statements to prevent SQL injection)
    // Return the role from the database
}
?>

05Prevention Checklist

Never trust client-supplied data for authorization.
Always verify security-critical decisions against server-side, authoritative sources: session data, database records, or cryptographic signatures you control.
Use server-side sessions.
Store user identity and permissions in a session token (e.g., a signed JWT or opaque session ID) that the client cannot forge or modify.
Validate every request.
Before granting access or performing a sensitive action, re-check the user's identity and permissions on the server, even if the client provided them in a previous request.
Implement proper access control.
Use a centralized authorization function or middleware that checks permissions before allowing access to resources or actions.
Avoid hidden form fields for security.
Never use <input type="hidden"> fields to store roles, user IDs, or permission flags; these are trivially modifiable by the client.
Log and monitor access decisions.
Track failed authorization attempts and unusual access patterns to detect tampering.

06Signs You May Already Be Affected

Look for unexpected privilege escalation reports, users accessing data or features they shouldn't have access to, or audit logs showing actions performed by users who claim they didn't perform them. If you find that changing a URL parameter or form field allows a user to access another user's data or gain admin privileges, this weakness is present.

07Related Recent Vulnerabilities