Weakness reference
CWE-283

Unverified Ownership

This weakness occurs when software allows an operation on a resource—such as a file, database record, or user account—without first confirming that the person…

01Summary

This weakness occurs when software allows an operation on a resource—such as a file, database record, or user account—without first confirming that the person requesting the operation actually owns or has permission to access that resource. An attacker can exploit this to modify, delete, or read data belonging to other users.

02How It Happens

The vulnerability typically arises when a developer assumes that because a user is authenticated (logged in), they are automatically authorized to perform any action they request. The code may accept a resource identifier (like a user ID, file path, or record number) directly from user input and act on it without checking whether the authenticated user owns that resource. This is especially common in REST APIs and web applications where resource IDs are passed as URL parameters or form fields.

For example, if a user can change their own password by submitting their user ID, but the application doesn't verify that the submitted ID matches the logged-in user's ID, an attacker can change anyone's password by simply guessing or enumerating user IDs.

03Real-World Impact

An attacker can read, modify, or delete data belonging to other users. In a financial application, this could mean transferring funds between accounts. In a content management system, it could mean deleting or altering other users' posts. In a multi-tenant SaaS platform, it could mean accessing another organization's confidential data. The impact ranges from privacy violations to complete account takeover.

04Vulnerable & Fixed Patterns

Vulnerable pattern
@app.route('/api/user/<user_id>/profile', methods=['PUT'])
def update_profile(user_id):
    data = request.get_json()
    user = User.query.get(user_id)
    user.email = data['email']
    user.name = data['name']
    db.session.commit()
    return {'status': 'updated'}

Why it's vulnerable:
The function accepts a user_id from the URL and updates that user's profile without checking whether the authenticated user is the owner of that profile. An attacker can change any user's data by modifying the user_id parameter.

Fixed pattern
@app.route('/api/user/<user_id>/profile', methods=['PUT'])
def update_profile(user_id):
    current_user = get_current_user()  # from session/token
    if current_user.id != int(user_id):
        return {'error': 'Unauthorized'}, 403
    data = request.get_json()
    user = User.query.get(user_id)
    user.email = data['email']
    user.name = data['name']
    db.session.commit()
    return {'status': 'updated'}
Vulnerable pattern
<?php
if (isset($_POST['user_id']) && isset($_POST['new_email'])) {
    $user_id = $_POST['user_id'];
    $new_email = $_POST['new_email'];
    $wpdb->update('wp_users', 
        array('user_email' => $new_email), 
        array('ID' => $user_id)
    );
    echo "Email updated";
}
?>

Why it's vulnerable:
The code accepts a user ID directly from POST data and updates the email without verifying that the logged-in user owns that account. Any authenticated user can change any other user's email.

Fixed pattern
<?php
$current_user_id = get_current_user_id();
if (isset($_POST['user_id']) && isset($_POST['new_email'])) {
    $user_id = intval($_POST['user_id']);
    if ($current_user_id !== $user_id) {
        wp_die('Unauthorized', 403);
    }
    $new_email = sanitize_email($_POST['new_email']);
    $wpdb->update('wp_users', 
        array('user_email' => $new_email), 
        array('ID' => $user_id)
    );
    echo "Email updated";
}
?>

05Prevention Checklist

Always retrieve the current user's identity from the session or authentication token, never from user input.
Before performing any operation on a resource, explicitly verify that the authenticated user owns or has permission to access that resource.
Use allowlists of permitted actions for each user role; deny by default.
In REST APIs, avoid exposing sequential or easily guessable resource IDs; use UUIDs or opaque tokens where possible.
Log all access attempts to sensitive resources, especially failed authorization checks, to detect enumeration attacks.
Test authorization logic separately from authentication; ensure that being logged in does not grant access to all resources.

06Signs You May Already Be Affected

Check your application logs for requests where a user attempts to access or modify resources with IDs that don't belong to them. Look for patterns of sequential ID enumeration (e.g., requests to /api/user/1, /api/user/2, /api/user/3). Review recent changes to user accounts, files, or records to see if any were modified by users who should not have had access.

07Related Recent Vulnerabilities