Weakness reference
CWE-284

Improper Access Control

Improper access control occurs when software fails to enforce or correctly implements restrictions on who can access specific resources, features, or data…

01Summary

Improper access control occurs when software fails to enforce or correctly implements restrictions on who can access specific resources, features, or data. This allows unauthorized users to perform actions or view information they should not be able to reach. It is one of the most common and impactful vulnerability categories because it directly undermines the security model of an application.

02How It Happens

Access control weaknesses arise when developers assume that hiding a feature (e.g., not linking to it in the UI) is sufficient protection, or when they fail to validate permissions on the server side before granting access. Common patterns include:

- Missing authorization checks:
A function or endpoint processes a request without verifying the user's role or permissions. - Relying on client-side enforcement:
JavaScript or form fields that hide or disable options, but the server accepts requests anyway. - Broken object references:
Users can access resources by guessing or manipulating identifiers (e.g., /user/profile?id=123 works for any ID without checking ownership). - Inconsistent permission logic:
Different parts of the application enforce different rules, or rules are applied inconsistently across similar features. - Default or overly permissive settings:
Resources are world-readable or world-writable by default, or roles grant more permissions than intended.

03Real-World Impact

Improper access control can lead to unauthorized data disclosure, modification, or deletion. An attacker might view confidential customer records, modify another user's profile, escalate their own privileges to administrator, or delete critical data. In regulated industries, this can result in compliance violations, fines, and loss of customer trust. The impact scales with the sensitivity of the protected resource.

04Vulnerable & Fixed Patterns

Vulnerable pattern
from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route('/api/user/<user_id>/data', methods=['GET'])
def get_user_data(user_id):
    # No check that the requesting user owns this user_id
    user_record = database.query(f"SELECT * FROM users WHERE id = {user_id}")
    return jsonify(user_record)

Why it's vulnerable:
The endpoint accepts any user_id and returns the data without verifying that the authenticated user has permission to access that specific record. An attacker can enumerate user IDs and retrieve anyone's data.

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

app = Flask(__name__)

@app.route('/api/user/<user_id>/data', methods=['GET'])
def get_user_data(user_id):
    # Verify the requesting user is authenticated
    if 'user_id' not in session:
        return jsonify({'error': 'Unauthorized'}), 401
    
    # Verify the requesting user owns this record
    if session['user_id'] != int(user_id):
        return jsonify({'error': 'Forbidden'}), 403
    
    user_record = database.query("SELECT * FROM users WHERE id = ?", (user_id,))
    return jsonify(user_record)
Vulnerable pattern
<?php
// Assume user is logged in via $_SESSION['user_id']

if (isset($_GET['profile_id'])) {
    $profile_id = $_GET['profile_id'];
    $result = mysqli_query($conn, "SELECT * FROM profiles WHERE id = $profile_id");
    $profile = mysqli_fetch_assoc($result);
    echo "Name: " . $profile['name'];
    echo "Email: " . $profile['email'];
}
?>

Why it's vulnerable:
The code retrieves and displays any profile by ID without checking whether the logged-in user has permission to view it. An attacker can change the profile_id parameter to access other users' profiles.

Fixed pattern
<?php
if (!isset($_SESSION['user_id'])) {
    http_response_code(401);
    die('Unauthorized');
}

if (isset($_GET['profile_id'])) {
    $profile_id = intval($_GET['profile_id']);
    $user_id = $_SESSION['user_id'];
    
    // Verify ownership or explicit permission
    $result = mysqli_query($conn, 
        "SELECT * FROM profiles WHERE id = ? AND user_id = ?",
        [$profile_id, $user_id]
    );
    
    if ($result && mysqli_num_rows($result) > 0) {
        $profile = mysqli_fetch_assoc($result);
        echo "Name: " . esc_html($profile['name']);
        echo "Email: " . esc_html($profile['email']);
    } else {
        http_response_code(403);
        die('Forbidden');
    }
}
?>

05Prevention Checklist

Enforce authorization on every endpoint and function:
Check user permissions server-side before returning data or performing actions, regardless of whether the UI hides the feature.
Use a consistent permission model:
Define roles and permissions clearly, and apply them uniformly across the application. Use a library or framework feature (e.g., middleware, decorators) to avoid duplication.
Verify resource ownership:
When a user requests a resource by ID, confirm they own it or have explicit permission to access it before returning it.
Default to deny:
Start with the most restrictive permissions and grant access only where explicitly needed, rather than starting permissive.
Test access control:
Write tests that verify unauthorized users cannot access protected resources, and that users can only access their own data.
Log and monitor access:
Record who accesses what, and alert on unusual patterns (e.g., a user accessing many other users' records).

06Signs You May Already Be Affected

Look for unexpected access to sensitive data or features by users who should not have permission. Check application logs for requests to endpoints or resources that should be restricted, or for users accessing records they do not own. Review user roles and permissions in your database to identify overly broad assignments or default accounts with excessive privileges.

07Related Recent Vulnerabilities