Improper authorization occurs when software fails to correctly verify whether a user has permission to perform a requested action or access a resource. This…
Improper authorization occurs when software fails to correctly verify whether a user has permission to perform a requested action or access a resource. This weakness allows attackers to bypass access controls and perform actions they should not be allowed to do — such as viewing other users' data, modifying records, or accessing administrative functions. It is one of the most common and impactful security flaws in web applications.
02How It Happens
Authorization flaws typically arise when developers assume that reaching a certain code path means a user is authorized, without explicitly checking permissions. Common patterns include: relying on client-side checks alone (which can be bypassed), checking only whether a user is logged in rather than whether they have the right role or permission, failing to validate that a resource ID belongs to the current user before returning it, or inconsistently applying permission checks across different endpoints or functions. The flaw is often introduced when authorization logic is added late in development or when different developers implement it differently across a codebase.
03Real-World Impact
An improper authorization flaw can lead to unauthorized data access, account takeover, privilege escalation, or modification of sensitive records. For example, an attacker might view another user's private information, change someone else's password, delete records they don't own, or gain administrative access. The impact scales with the sensitivity of the data or actions involved — a flaw in a banking application is far more severe than one in a public forum.
04Vulnerable & Fixed Patterns
Vulnerable pattern
@app.route('/user/<user_id>/profile')
def get_user_profile(user_id):
user = db.query(User).filter(User.id == user_id).first()
if user:
return jsonify(user.to_dict())
return "Not found", 404
Why it's vulnerable: The endpoint retrieves and returns any user's profile based on the ID in the URL, without checking whether the logged-in user has permission to view that specific profile. An attacker can simply change the user_id parameter to access anyone's data.
Fixed pattern
@app.route('/user/<user_id>/profile')
def get_user_profile(user_id):
current_user = get_current_user() # from session/token
if not current_user:
return "Unauthorized", 401
user = db.query(User).filter(User.id == user_id).first()
if not user:
return "Not found", 404
# Check: user can only view their own profile, or is an admin
if user.id != current_user.id and not current_user.is_admin:
return "Forbidden", 403
return jsonify(user.to_dict())
Vulnerable pattern
<?php
if (isset($_GET['post_id'])) {
$post_id = $_GET['post_id'];
$result = mysqli_query($conn, "SELECT * FROM posts WHERE id = $post_id");
$post = mysqli_fetch_assoc($result);
echo "Title: " . $post['title'];
}
?>
Why it's vulnerable: The code retrieves and displays any post by ID without verifying that the current user owns it or has permission to view it. An attacker can request any post ID and read its contents.
Fixed pattern
<?php
if (isset($_GET['post_id'])) {
$post_id = intval($_GET['post_id']);
$user_id = $_SESSION['user_id'] ?? null;
if (!$user_id) {
http_response_code(401);
die("Unauthorized");
}
$stmt = $conn->prepare("SELECT * FROM posts WHERE id = ? AND user_id = ?");
$stmt->bind_param("ii", $post_id, $user_id);
$stmt->execute();
$result = $stmt->get_result();
$post = $result->fetch_assoc();
if (!$post) {
http_response_code(403);
die("Forbidden");
}
echo "Title: " . htmlspecialchars($post['title']);
}
?>
05Prevention Checklist
Verify identity first: Ensure the user is authenticated (logged in) before checking permissions.
Check permissions on every access: Do not rely on client-side checks or assume that reaching a code path means authorization is granted. Validate permissions server-side for every request.
Use a consistent authorization model: Define roles and permissions clearly (e.g., owner, editor, viewer, admin) and apply them uniformly across all endpoints and functions.
Validate resource ownership: When a user requests a resource by ID, verify that the resource belongs to them or that they have explicit permission to access it.
Deny by default: If a permission is not explicitly granted, deny access. Do not assume access is allowed unless proven otherwise.
Log authorization failures: Record failed authorization attempts to detect and investigate suspicious activity.
06Signs You May Already Be Affected
Look for unexpected access to data or functions: users reporting that they can see other users' private information, unauthorized changes to records, or users gaining access to administrative features. Check access logs for requests to endpoints with IDs that don't belong to the requesting user, or for repeated 403 (Forbidden) responses that may indicate probing. Review user feedback and support tickets for mentions of seeing data they shouldn't have access to.