Weakness reference
CWE-187

Partial String Comparison

Partial string comparison occurs when code checks only the beginning or a limited portion of a string instead of validating the entire value. This allows…

01Summary

Partial string comparison occurs when code checks only the beginning (or a limited portion) of a string instead of validating the entire value. This allows attackers to bypass authentication, authorization, or validation checks by crafting input that matches the checked portion while containing malicious content elsewhere. It's a common mistake in token validation, URL checking, and permission verification.

02How It Happens

Developers sometimes use string functions that check only a prefix or substring, assuming the partial match is sufficient for security. For example, checking whether a token *starts with* an expected value, or whether a URL *contains* a domain name, rather than verifying an exact match or using a complete comparison. This often happens when developers misunderstand the scope of a function (e.g., confusing startswith() with full equality), or when they manually slice strings and compare only the slice. The vulnerability is especially common in token validation, API key checking, and URL-based access control.

03Real-World Impact

An attacker can forge or modify input to pass a partial check while retaining control over the unchecked portion. In authentication contexts, this might allow login with a valid username prefix plus arbitrary characters. In authorization, it could permit access to unintended resources by matching a partial path or domain. In token validation, an attacker might append data to a valid token prefix, bypassing expiration or scope checks. The severity depends on what the partial check protects—it can range from account takeover to privilege escalation.

04Vulnerable & Fixed Patterns

Vulnerable pattern
def validate_api_token(provided_token, expected_prefix):
    # Vulnerable: only checks if token starts with expected prefix
    if provided_token.startswith(expected_prefix):
        return True
    return False

# Attacker can pass "valid_prefix_malicious_data" and bypass the check
user_token = "valid_prefix_malicious_data"
if validate_api_token(user_token, "valid_prefix"):
    grant_access()

Why it's vulnerable:
The function only verifies that the token begins with the expected prefix, not that it matches the entire expected token. An attacker can append arbitrary data and still pass validation.

Fixed pattern
def validate_api_token(provided_token, expected_token):
    # Fixed: compares the entire token using constant-time comparison
    import hmac
    return hmac.compare_digest(provided_token, expected_token)

# Now only the exact token is accepted
user_token = "valid_prefix_malicious_data"
if validate_api_token(user_token, "valid_prefix"):
    grant_access()  # This will not execute
Vulnerable pattern
<?php
function check_user_role($user_id, $required_role) {
    $user_role = get_user_role($user_id);
    // Vulnerable: only checks if role starts with required prefix
    if (strpos($user_role, $required_role) === 0) {
        return true;
    }
    return false;
}

// Attacker with role "admin_readonly" passes check for "admin"
if (check_user_role($user_id, "admin")) {
    perform_admin_action();
}
?>

Why it's vulnerable:
strpos() with a position check of === 0 only verifies the role *begins* with the required string. A role like "admin_readonly" or "admin_fake" would pass a check for "admin".

Fixed pattern
<?php
function check_user_role($user_id, $required_role) {
    $user_role = get_user_role($user_id);
    // Fixed: compares the entire role string
    if ($user_role === $required_role) {
        return true;
    }
    return false;
}

// Now only exact role match is accepted
if (check_user_role($user_id, "admin")) {
    perform_admin_action();
}
?>

05Prevention Checklist

Use exact string equality (== or === in PHP, == in Python) instead of substring or prefix checks for security-critical comparisons.
For token or credential validation, use constant-time comparison functions (e.g., hmac.compare_digest() in Python, hash_equals() in PHP) to prevent timing attacks.
Validate the *entire* value, not just a portion; if you need to check a prefix, do so *in addition to* a full equality check, not instead of it.
Avoid manual string slicing for security checks; use built-in equality operators or dedicated validation libraries.
Review all authentication, authorization, and token-validation code for substring or prefix checks that should be full comparisons.

06Signs You May Already Be Affected

Look for authentication or authorization bypasses in logs—users accessing resources they shouldn't, or login attempts succeeding with malformed credentials. Check for unusual token values in access logs that contain extra characters or data appended to valid tokens. Review code for uses of startswith(), strpos(), substr(), or similar functions in security-critical paths; these are red flags if they're not paired with a full equality check.

07Related Recent Vulnerabilities