Weakness reference
CWE-269

Improper Privilege Management

Improper privilege management occurs when software fails to correctly assign, enforce, or verify user permissions. This allows users to access features, data…

01Summary

Improper privilege management occurs when software fails to correctly assign, enforce, or verify user permissions. This allows users to access features, data, or functionality they should not be able to reach. The weakness can range from a user viewing another's private information to an attacker gaining administrative control over an entire system.

02How It Happens

Privilege management flaws typically arise when developers assume a user's role or permission level without verifying it at the point of access. Common patterns include checking permissions only at the UI layer (which can be bypassed), failing to re-verify permissions on sensitive operations, hardcoding role checks, or using indirect identifiers (like user IDs in URLs) without confirming the requester has permission to access that resource. The vulnerability often emerges when privilege logic is scattered across the codebase rather than centralized, making it easy to miss a check in one code path.

03Real-World Impact

An attacker exploiting privilege management flaws can view, modify, or delete data belonging to other users; escalate their own account to administrator status; access restricted administrative panels; or trigger sensitive operations (password resets, fund transfers, account deletions) on behalf of other users. In multi-tenant systems, the impact can extend to cross-tenant data exposure. The severity depends on what functionality is unprotected—a missed check on a read-only report is less critical than one on account deletion or payment processing.

04Vulnerable & Fixed Patterns

Vulnerable pattern
def view_user_profile(user_id):
    # Assumes the request is from an authenticated user,
    # but does NOT verify they have permission to view this profile
    user = database.query(f"SELECT * FROM users WHERE id = {user_id}")
    return render_template("profile.html", user=user)

@app.route("/admin/delete_user/<int:user_id>", methods=["POST"])
def delete_user(user_id):
    # No privilege check — any authenticated user can delete anyone
    database.execute(f"DELETE FROM users WHERE id = {user_id}")
    return "User deleted"

Why it's vulnerable:
The code verifies that a user is logged in but never checks whether they own the profile they're viewing or whether they have admin rights to delete users. An attacker can simply change the user_id parameter to access or modify any account.

Fixed pattern
from functools import wraps
from flask import session, abort

def require_permission(permission):
    def decorator(f):
        @wraps(f)
        def decorated_function(*args, **kwargs):
            user_id = session.get("user_id")
            if not user_id or not has_permission(user_id, permission):
                abort(403)
            return f(*args, **kwargs)
        return decorated_function
    return decorator

@app.route("/user/profile/<int:user_id>")
def view_user_profile(user_id):
    current_user_id = session.get("user_id")
    # Verify the requester owns this profile or is an admin
    if user_id != current_user_id and not has_permission(current_user_id, "admin"):
        abort(403)
    user = database.query("SELECT * FROM users WHERE id = ?", (user_id,))
    return render_template("profile.html", user=user)

@app.route("/admin/delete_user/<int:user_id>", methods=["POST"])
@require_permission("admin")
def delete_user(user_id):
    database.execute("DELETE FROM users WHERE id = ?", (user_id,))
    return "User deleted"
Vulnerable pattern
<?php
// Assume user is logged in via $_SESSION["user_id"]

// View another user's profile — no permission check
if (isset($_GET["user_id"])) {
    $user_id = $_GET["user_id"];
    $result = $wpdb->get_row("SELECT * FROM wp_users WHERE ID = $user_id");
    echo "Name: " . $result->display_name;
}

// Admin function — no role verification
if (isset($_POST["delete_user"])) {
    $user_id = $_POST["user_id"];
    $wpdb->query("DELETE FROM wp_users WHERE ID = $user_id");
    echo "User deleted";
}
?>

Why it's vulnerable:
The code does not verify that the current user has permission to view or delete the target user. Any logged-in user can access any profile or delete any account by manipulating the user_id parameter.

Fixed pattern
<?php
function current_user_can($capability) {
    $user_id = $_SESSION["user_id"] ?? null;
    if (!$user_id) return false;
    $user = get_userdata($user_id);
    return user_has_cap($user, $capability);
}

// View another user's profile — verify permission
if (isset($_GET["user_id"])) {
    $user_id = intval($_GET["user_id"]);
    $current_user_id = $_SESSION["user_id"];
    
    // Allow viewing own profile or if user is admin
    if ($user_id !== $current_user_id && !current_user_can("manage_users")) {
        wp_die("Access denied", 403);
    }
    
    $result = $wpdb->get_row($wpdb->prepare(
        "SELECT * FROM wp_users WHERE ID = %d",
        $user_id
    ));
    echo "Name: " . esc_html($result->display_name);
}

// Admin function — verify role
if (isset($_POST["delete_user"])) {
    if (!current_user_can("delete_users")) {
        wp_die("Access denied", 403);
    }
    
    $user_id = intval($_POST["user_id"]);
    $wpdb->query($wpdb->prepare(
        "DELETE FROM wp_users WHERE ID = %d",
        $user_id
    ));
    echo "User deleted";
}
?>

05Prevention Checklist

Centralize privilege checks.
Create a single, reusable function or middleware that verifies permissions before sensitive operations; do not scatter checks throughout the codebase.
Verify permissions on every sensitive operation.
Never rely on UI-layer checks alone; always re-verify permissions server-side before reading, modifying, or deleting data.
Use role-based or attribute-based access control.
Define clear roles (admin, editor, viewer) or attributes (owns_resource, is_manager) and check them consistently.
Verify resource ownership.
When a user requests access to a resource (a post, a profile, a file), confirm they own it or have explicit permission to access it—do not assume the request is valid just because the user is authenticated.
Avoid indirect privilege inference.
Do not assume a user's role based on their user ID, URL path, or other indirect signals; always look up their actual permissions from a trusted source.
Log and monitor privilege escalation attempts.
Track failed permission checks and unusual privilege grants to detect potential attacks.

06Signs You May Already Be Affected

Check your application logs for repeated 403 (Forbidden) errors from the same user or IP, which may indicate someone probing for unprotected endpoints. Review your user and role tables for unexpected admin accounts or privilege grants that you did not authorize. If users report seeing data or functionality belonging to other accounts, or if you find unauthorized changes to sensitive records, privilege management flaws may be the cause.

07Related Recent Vulnerabilities