Weakness reference
CWE-275

Permission Issues

Permission issues occur when an application fails to properly assign, enforce, or validate access controls on sensitive resources. This can allow unauthorized…

01Summary

Permission issues occur when an application fails to properly assign, enforce, or validate access controls on sensitive resources. This can allow unauthorized users to view, modify, or delete data they shouldn't have access to, or perform actions reserved for administrators. Weak permission checks are among the most common causes of data breaches and account compromise.

02How It Happens

Permission issues arise when developers either omit access control checks entirely, implement them inconsistently, or fail to validate that the current user has the required privilege level before granting access to a resource. This often happens when a resource identifier (like a user ID, file path, or database record) is passed directly from user input without verifying ownership or role. The vulnerability can also occur when permissions are checked at the UI level only (e.g., hiding a button) rather than enforced server-side, or when default permissions are too permissive.

03Real-World Impact

An attacker can exploit permission issues to access or modify another user's account, view confidential documents, escalate their own privileges to administrator level, or delete critical data. In multi-tenant systems, weak permissions may allow one customer to access another's data. The impact ranges from privacy violations and data theft to complete system compromise, depending on what resources lack proper access controls.

04Vulnerable & Fixed Patterns

Vulnerable pattern
from flask import Flask, request, jsonify
import sqlite3

app = Flask(__name__)

@app.route('/api/user/<user_id>/profile', methods=['GET'])
def get_user_profile(user_id):
    conn = sqlite3.connect('app.db')
    cursor = conn.cursor()
    cursor.execute('SELECT name, email, phone FROM users WHERE id = ?', (user_id,))
    user = cursor.fetchone()
    conn.close()
    return jsonify({'name': user[0], 'email': user[1], 'phone': user[2]})

Why it's vulnerable:
The endpoint accepts any user_id from the URL and returns that user's profile without checking whether the authenticated user has permission to view it. An attacker can simply change the ID in the URL to access any other user's data.

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

app = Flask(__name__)

@app.route('/api/user/<user_id>/profile', methods=['GET'])
def get_user_profile(user_id):
    # Verify the requesting user is authenticated
    if 'user_id' not in session:
        return jsonify({'error': 'Unauthorized'}), 401
    
    # Verify the user can only access their own profile
    if session['user_id'] != int(user_id):
        return jsonify({'error': 'Forbidden'}), 403
    
    conn = sqlite3.connect('app.db')
    cursor = conn.cursor()
    cursor.execute('SELECT name, email, phone FROM users WHERE id = ?', (user_id,))
    user = cursor.fetchone()
    conn.close()
    return jsonify({'name': user[0], 'email': user[1], 'phone': user[2]})
Vulnerable pattern
<?php
session_start();
$user_id = $_GET['user_id'];

$conn = new mysqli('localhost', 'user', 'pass', 'appdb');
$result = $conn->query("SELECT name, email, phone FROM users WHERE id = $user_id");
$user = $result->fetch_assoc();

echo json_encode($user);
?>

Why it's vulnerable:
The script retrieves any user's profile based on the user_id parameter without verifying that the logged-in user has permission to view it. Additionally, the query is vulnerable to SQL injection, but the primary issue here is the missing access control check.

Fixed pattern
<?php
session_start();

// Verify user is authenticated
if (!isset($_SESSION['user_id'])) {
    http_response_code(401);
    echo json_encode(['error' => 'Unauthorized']);
    exit;
}

$requested_user_id = intval($_GET['user_id']);

// Verify the user can only access their own profile
if ($_SESSION['user_id'] !== $requested_user_id) {
    http_response_code(403);
    echo json_encode(['error' => 'Forbidden']);
    exit;
}

$conn = new mysqli('localhost', 'user', 'pass', 'appdb');
$stmt = $conn->prepare("SELECT name, email, phone FROM users WHERE id = ?");
$stmt->bind_param('i', $requested_user_id);
$stmt->execute();
$result = $stmt->get_result();
$user = $result->fetch_assoc();

echo json_encode($user);
?>

05Prevention Checklist

Check permissions on every resource access.
Before returning or modifying any data, verify that the authenticated user has the required role or ownership. Never rely on UI-level hiding alone.
Use role-based or attribute-based access control.
Define clear permission models (e.g., "users can only view their own records" or "only admins can delete posts") and enforce them consistently across all endpoints.
Validate user identity and session state.
Ensure the user is authenticated and that their session is valid before processing any request.
Default to deny.
If a permission is not explicitly granted, deny access. Avoid overly permissive defaults.
Test access control with multiple user roles.
Verify that a regular user cannot access admin functions, and that users cannot access each other's data.
Log and monitor permission denials.
Track failed access attempts to detect potential attacks or misconfigurations.

06Signs You May Already Be Affected

Check your application logs for repeated 403 (Forbidden) errors or unusual patterns of requests to user-specific endpoints with varying IDs. Review your user and admin accounts for unexpected entries or privilege escalations. If you discover that users can view or modify data belonging to other accounts by changing a URL parameter or ID field, you likely have a permission issue that needs immediate remediation.

07Related Recent Vulnerabilities