Incorrect authorization occurs when a system checks whether a user is allowed to perform an action, but the check itself is flawed—allowing unauthorized users…
Incorrect authorization occurs when a system checks whether a user is allowed to perform an action, but the check itself is flawed—allowing unauthorized users to access resources or perform actions they shouldn't. This is distinct from *missing* authorization checks; the check exists but is broken. It's one of the most common causes of account takeover, privilege escalation, and data exposure.
02How It Happens
Authorization flaws typically arise from logic errors in the permission-checking code. Common patterns include: comparing user roles or IDs against the wrong variable, checking permissions at the wrong layer (e.g., only on the client side), trusting user-supplied data to determine access level, or failing to re-verify permissions after a state change. For example, a developer might check if a user is an admin by looking at a cookie or URL parameter instead of a server-side session, or might verify access to one resource but forget to verify access to a related one.
The root cause is usually that the developer either misunderstood the access control requirements, didn't anticipate an edge case, or assumed that a prior check elsewhere in the code would prevent unauthorized requests from reaching the authorization logic.
03Real-World Impact
Incorrect authorization can lead to account takeover (accessing another user's profile or data), privilege escalation (a regular user gaining admin capabilities), unauthorized data modification or deletion, and exposure of sensitive information. In multi-tenant systems, it can allow one customer to access another's data. The impact scales with the sensitivity of the resource and the number of users affected.
04Vulnerable & Fixed Patterns
Vulnerable pattern
def get_user_profile(user_id):
# Authorization check uses user_id from request, not verified session
if request.args.get('user_id') == session.get('user_id'):
user = db.query(User).filter_by(id=user_id).first()
return user.to_dict()
else:
return {"error": "Unauthorized"}, 403
Why it's vulnerable: The code compares request.args.get('user_id') (attacker-controlled) against the session, but then queries the database using the original user_id parameter. An attacker can modify the URL parameter to access any user's profile.
Fixed pattern
def get_user_profile(user_id):
# Authorization check uses verified session identity
if int(user_id) != session.get('user_id'):
return {"error": "Unauthorized"}, 403
user = db.query(User).filter_by(id=user_id).first()
if not user:
return {"error": "Not found"}, 404
return user.to_dict()
Vulnerable pattern
<?php
function delete_post($post_id) {
// Check if user is logged in, but don't verify ownership
if (!isset($_SESSION['user_id'])) {
die("Unauthorized");
}
$post = $wpdb->get_row("SELECT * FROM posts WHERE id = $post_id");
$wpdb->query("DELETE FROM posts WHERE id = $post_id");
echo "Post deleted";
}
?>
Why it's vulnerable: The code only checks that *someone* is logged in, not that the logged-in user owns the post. Any authenticated user can delete any post.
Fixed pattern
<?php
function delete_post($post_id) {
if (!isset($_SESSION['user_id'])) {
die("Unauthorized");
}
$post = $wpdb->get_row($wpdb->prepare(
"SELECT * FROM posts WHERE id = %d",
$post_id
));
if (!$post || $post->author_id != $_SESSION['user_id']) {
die("Unauthorized");
}
$wpdb->query($wpdb->prepare(
"DELETE FROM posts WHERE id = %d",
$post_id
));
echo "Post deleted";
}
?>
05Prevention Checklist
Verify authorization on every sensitive action , not just some. Use a centralized authorization function or middleware to enforce this consistently.
Always check against server-side session or token data , never trust user-supplied identifiers, cookies, or URL parameters to determine access level.
Verify ownership or role at the resource level , not just at the endpoint level. Before returning or modifying a resource, confirm the current user has permission for *that specific resource*.
Use allowlists, not blocklists. Define what users *can* do, rather than what they can't.
Test authorization logic with multiple user roles and cross-user scenarios. Attempt to access another user's data or perform actions as a lower-privileged user.
Log authorization failures and monitor for patterns (e.g., repeated 403 errors from the same IP) that may indicate an attack.
06Signs You May Already Be Affected
Look for unexpected access to user data or admin functions by non-admin accounts, unusual changes to resources you don't own, or log entries showing authorization failures followed by successful requests. If you notice users reporting that others can see or modify their data, or if a regular user suddenly has admin capabilities, investigate authorization logic immediately.