This weakness occurs when an application allows users to directly control identifiers like user IDs, account numbers, or resource IDs in requests, without…
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.