Missing authorization occurs when a system verifies who a user is authentication but fails to check what they are allowed to do authorization. An authenticated…
Missing authorization occurs when a system verifies *who* a user is (authentication) but fails to check *what they are allowed to do* (authorization). An authenticated user can access or modify resources, perform actions, or view data they should not have permission to reach. This is a critical flaw because authentication alone does not guarantee security — a logged-in attacker can escalate privileges or access other users' data.
02How It Happens
Authorization checks are typically implemented as conditional logic that compares a user's role, permission level, or ownership status against the requested resource or action. When this check is missing or incomplete, the application assumes that any authenticated user can perform any action. This often happens when developers conflate authentication with authorization, or when permission checks are added inconsistently across an application. For example, a function may verify that a user is logged in but never verify that the logged-in user owns the record they are trying to delete, or that they hold the admin role required to access a settings page.
03Real-World Impact
An attacker with a valid account can view, modify, or delete other users' data; escalate their own privileges to administrator; access sensitive reports or configuration pages; or perform administrative actions without authorization. In multi-tenant systems, one customer's data may become visible to another. In financial or healthcare applications, unauthorized access can expose confidential information or enable fraudulent transactions. The damage is often severe because the attacker is already inside the system and trusted by the authentication layer.
04Vulnerable & Fixed Patterns
Vulnerable pattern
from flask import Flask, request, session
import sqlite3
app = Flask(__name__)
@app.route('/user/<user_id>/profile', methods=['GET'])
def get_user_profile(user_id):
if 'user_id' not in session:
return "Unauthorized", 401
conn = sqlite3.connect('app.db')
cursor = conn.cursor()
cursor.execute('SELECT * FROM users WHERE id = ?', (user_id,))
profile = cursor.fetchone()
return profile
Why it's vulnerable: The code checks that a user is logged in (session['user_id'] exists) but never verifies that the logged-in user is allowed to view the requested user_id. Any authenticated user can view any other user's profile by changing the URL parameter.
Fixed pattern
from flask import Flask, request, session
import sqlite3
app = Flask(__name__)
@app.route('/user/<user_id>/profile', methods=['GET'])
def get_user_profile(user_id):
if 'user_id' not in session:
return "Unauthorized", 401
# Authorization check: user can only view their own profile
if session['user_id'] != int(user_id):
return "Forbidden", 403
conn = sqlite3.connect('app.db')
cursor = conn.cursor()
cursor.execute('SELECT * FROM users WHERE id = ?', (user_id,))
profile = cursor.fetchone()
return profile
Vulnerable pattern
<?php
session_start();
if (!isset($_SESSION['user_id'])) {
http_response_code(401);
exit('Unauthorized');
}
$post_id = $_GET['post_id'];
$mysqli = new mysqli('localhost', 'user', 'pass', 'blog_db');
$result = $mysqli->query("SELECT * FROM posts WHERE id = $post_id");
$post = $result->fetch_assoc();
echo "Title: " . esc_html($post['title']);
?>
Why it's vulnerable: The code verifies the user is logged in but does not check whether the logged-in user owns or has permission to view the post. Any authenticated user can read any post by guessing or iterating post IDs.
Fixed pattern
<?php
session_start();
if (!isset($_SESSION['user_id'])) {
http_response_code(401);
exit('Unauthorized');
}
$post_id = intval($_GET['post_id']);
$user_id = $_SESSION['user_id'];
$mysqli = new mysqli('localhost', 'user', 'pass', 'blog_db');
// Authorization check: verify user owns or has permission to view this post
$result = $mysqli->query(
"SELECT * FROM posts WHERE id = $post_id AND author_id = $user_id"
);
if ($result->num_rows === 0) {
http_response_code(403);
exit('Forbidden');
}
$post = $result->fetch_assoc();
echo "Title: " . esc_html($post['title']);
?>
05Prevention Checklist
Define a permission model. Document which roles or user types can perform which actions on which resources (e.g., "users can edit their own profile; admins can edit any profile").
Check authorization on every sensitive action. Before returning data or modifying state, verify the user's role, ownership, or explicit permission. Do not rely on client-side checks or obscurity.
Use a centralized authorization function or middleware. Implement a reusable function (e.g., can_user_access(user, resource)) to avoid inconsistent checks across the codebase.
Verify ownership or role, not just authentication. Compare the user's ID or role against the resource's owner or required permission level.
Test authorization boundaries. Write tests that confirm a user cannot access resources they should not (e.g., another user's data, admin pages without admin role).
Log authorization failures. Record failed authorization attempts to detect reconnaissance or privilege escalation attempts.
06Signs You May Already Be Affected
Look for unexpected access to sensitive pages or data by low-privilege accounts, or reports from users that they can see or modify other users' information. Check application logs for repeated requests to resources with different IDs (e.g., /user/1/profile, /user/2/profile, /user/3/profile from a single session), which may indicate an attacker enumerating data. Review admin or settings pages to confirm they are not accessible to regular users.