This weakness occurs when software compares two entities users, files, requests, etc. using incomplete or insufficient criteria, leading it to treat unrelated…
This weakness occurs when software compares two entities (users, files, requests, etc.) using incomplete or insufficient criteria, leading it to treat unrelated or distinct entities as if they were the same. The result is a logic flaw where security decisions are made based on an incomplete comparison, potentially allowing an attacker to bypass access controls or impersonate another entity.
02How It Happens
Security-critical comparisons often need to check multiple attributes to ensure two entities are truly identical. When a developer compares only a subset of those attributes—or uses attributes that are not unique enough—the comparison fails to distinguish between different entities. For example, comparing only a username without also checking the associated account ID, or comparing a file path without verifying its full canonical form, can allow an attacker to exploit the incomplete logic. This is especially dangerous in authentication, authorization, and resource-access decisions where the stakes are high.
03Real-World Impact
An attacker exploiting this weakness can gain unauthorized access to resources, impersonate other users, or bypass authentication checks. For instance, if a system compares user sessions using only a username (ignoring session tokens or timestamps), an attacker might reuse an old session or hijack a concurrent session. Similarly, if file access is checked using only a filename without resolving symlinks or path traversal sequences, an attacker could access files outside the intended directory. The severity depends on what decision is being made based on the incomplete comparison.
04Vulnerable & Fixed Patterns
Vulnerable pattern
def authorize_user_action(user_input, allowed_users):
# Vulnerable: comparing only the username, ignoring user ID or account context
if user_input in allowed_users:
return True
return False
# An attacker could craft user_input = "admin" if "admin" is in the list,
# even if they are not the actual admin account (e.g., a different "admin" user)
Why it's vulnerable: The comparison uses only the username string, which may not be unique across different user accounts or contexts. A user named "admin" in one organization could be confused with "admin" in another.
Fixed pattern
def authorize_user_action(user_id, user_context, allowed_user_ids):
# Fixed: compare both user ID and context (e.g., organization, account)
if user_id in allowed_user_ids and user_context == get_user_context(user_id):
return True
return False
Vulnerable pattern
<?php
function check_file_access($requested_file, $allowed_files) {
// Vulnerable: comparing only the filename, not the full canonical path
$filename = basename($requested_file);
if (in_array($filename, $allowed_files)) {
return true;
}
return false;
}
// An attacker could request "/var/www/../../etc/passwd"
// and if "passwd" is in $allowed_files, access would be granted
?>
Why it's vulnerable: The comparison uses only the filename, ignoring the full path. An attacker can use path traversal sequences to access files outside the intended directory.
Fixed pattern
<?php
function check_file_access($requested_file, $allowed_files, $base_dir) {
// Fixed: resolve to canonical path and compare the full path
$canonical_path = realpath($requested_file);
$base_canonical = realpath($base_dir);
if ($canonical_path && strpos($canonical_path, $base_canonical) === 0) {
if (in_array($canonical_path, $allowed_files)) {
return true;
}
}
return false;
}
?>
05Prevention Checklist
Identify all distinguishing factors: For any security-critical comparison, document what makes two entities truly different (user ID + account context, full canonical path, session token + timestamp, etc.).
Use unique, immutable identifiers: Prefer comparing database IDs or cryptographic tokens rather than user-supplied strings like usernames or filenames.
Canonicalize before comparing: Resolve paths, normalize URLs, and decode encoded input to their canonical form before comparison.
Compare all relevant attributes: Never rely on a single field; include context, scope, and ownership in the comparison logic.
Test with edge cases: Verify that your comparison correctly rejects similar-looking but distinct entities (e.g., "user1" vs. "user11", or symlinks vs. real files).
Use allowlists with full context: If using an allowlist, ensure each entry includes all necessary context, not just a partial identifier.
06Signs You May Already Be Affected
Look for unexpected access to resources by users who should not have permission, or reports of users accessing each other's data or accounts. Check authentication logs for sessions that persist longer than expected or reappear after logout. Review file-access logs for requests to files outside the intended directory, or for repeated access attempts using path traversal patterns.