Weakness reference
CWE-639

Authorization Bypass Through User-Controlled Key

This weakness occurs when an application allows users to directly control identifiers like user IDs, account numbers, or resource IDs in requests, without…

01Summary

This weakness occurs when an application allows users to directly control identifiers (like user IDs, account numbers, or resource IDs) in requests, without verifying that the requester has permission to access that specific resource. An attacker can simply change the ID in a URL or form field to access or modify another user's data.

02How It Happens

Applications often use identifiers to fetch or modify resources—a user ID in a URL parameter, an account number in a form field, or a resource ID in an API request. If the application trusts these identifiers without checking whether the current user owns or has permission to access that resource, an attacker can enumerate or guess valid IDs and gain unauthorized access. The vulnerability is especially common in REST APIs and web applications that pass IDs directly to the database query without an authorization check.

03Real-World Impact

An attacker can view, modify, or delete another user's profile, financial records, medical information, or other sensitive data. In some cases, this leads to account takeover, fraud, or privacy violations. The impact scales with the sensitivity of the data and the number of users affected—a single authorization bypass in a banking application could expose thousands of accounts.

04Vulnerable & Fixed Patterns

Vulnerable pattern
from flask import Flask, request
import sqlite3

app = Flask(__name__)

@app.route('/user/<user_id>')
def get_user(user_id):
    conn = sqlite3.connect('app.db')
    cursor = conn.cursor()
    cursor.execute('SELECT * FROM users WHERE id = ?', (user_id,))
    user = cursor.fetchone()
    return {'name': user[1], 'email': user[2]}

Why it's vulnerable:
The function accepts a user_id from the URL and retrieves the user record without checking whether the logged-in user has permission to view that ID. An attacker can change the ID to any number and access any user's data.

Fixed pattern
from flask import Flask, request, session
import sqlite3

app = Flask(__name__)

@app.route('/user/<user_id>')
def get_user(user_id):
    # Verify the logged-in user is requesting their own data
    if int(user_id) != session.get('user_id'):
        return {'error': 'Unauthorized'}, 403
    
    conn = sqlite3.connect('app.db')
    cursor = conn.cursor()
    cursor.execute('SELECT * FROM users WHERE id = ?', (user_id,))
    user = cursor.fetchone()
    return {'name': user[1], 'email': user[2]}
Vulnerable pattern
<?php
session_start();
$user_id = $_GET['user_id'];

$conn = new mysqli('localhost', 'user', 'pass', 'app_db');
$result = $conn->query("SELECT * FROM users WHERE id = " . intval($user_id));
$user = $result->fetch_assoc();

echo "Name: " . $user['name'] . "<br>";
echo "Email: " . $user['email'];
?>

Why it's vulnerable:
The script retrieves a user record based on the user_id GET parameter without verifying that the logged-in user owns or has permission to access that record.

Fixed pattern
<?php
session_start();
$user_id = $_GET['user_id'];

// Verify the logged-in user is requesting their own data
if (intval($user_id) !== $_SESSION['user_id']) {
    http_response_code(403);
    echo "Unauthorized";
    exit;
}

$conn = new mysqli('localhost', 'user', 'pass', 'app_db');
$result = $conn->query("SELECT * FROM users WHERE id = " . intval($user_id));
$user = $result->fetch_assoc();

echo "Name: " . esc_html($user['name']) . "<br>";
echo "Email: " . esc_html($user['email']);
?>

05Prevention Checklist

Always verify ownership or permission
before returning or modifying a resource. Compare the requested ID against the logged-in user's ID or role.
Use indirect references
where possible—map user-visible IDs to internal identifiers server-side, so users never see or control the actual primary key.
Implement role-based access control (RBAC)
so that authorization checks are consistent across all endpoints and resources.
Log and monitor access patterns
for unusual ID enumeration (e.g., sequential requests to many user IDs in a short time).
Test authorization on every endpoint
that accepts an ID parameter—use automated security testing to catch missing checks.
Use framework-provided authorization decorators or middleware
rather than writing custom checks in each function.

06Signs You May Already Be Affected

Check your application logs for patterns of sequential or unusual ID requests from a single user or IP address. Review your access logs for requests to resources that should not be visible to the requester (e.g., admin endpoints accessed by non-admin users). If you find unexpected data modifications or access to accounts you don't recognize, an authorization bypass may have been exploited.

07Related Recent Vulnerabilities