Weakness reference
CWE-347

Improper Verification of Cryptographic Signature

This weakness occurs when software fails to properly verify cryptographic signatures on data, or skips signature verification entirely. An attacker can forge…

01Summary

This weakness occurs when software fails to properly verify cryptographic signatures on data, or skips signature verification entirely. An attacker can forge or tamper with signed data, and the application will accept it as legitimate. This undermines the core security guarantee that signatures are meant to provide: proof that data hasn't been altered and came from a trusted source.

02How It Happens

Cryptographic signatures work by combining data with a private key to produce a unique fingerprint that only the holder of that key could create. Verification uses the corresponding public key to confirm the signature matches the data. This weakness arises when developers either omit the verification step altogether, implement it incorrectly (e.g., checking only part of the signature, or using the wrong key), or disable signature checks during development and forget to re-enable them. Common mistakes include comparing signatures as strings instead of using constant-time comparison, verifying the signature format but not the actual cryptographic validity, or accepting multiple key sources without proper validation.

03Real-World Impact

Forged signatures can allow attackers to impersonate trusted parties, inject malicious code into signed packages or updates, tamper with financial transactions, or bypass authentication mechanisms that rely on signed tokens. The severity depends on what the signature protects: a forged software update could compromise every system that installs it; a forged authentication token could grant unauthorized access to user accounts; a forged message could enable fraud or social engineering at scale.

04Vulnerable & Fixed Patterns

Vulnerable pattern
import hashlib
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives import hashes

# Vulnerable: signature verification is skipped
def process_signed_message(message, signature, public_key):
    # Developer intended to verify but commented it out during testing
    # signature_valid = public_key.verify(
    #     signature,
    #     message,
    #     padding.PSS(padding.MGF1(hashes.SHA256()), padding.PSS.MAX_LENGTH),
    #     hashes.SHA256()
    # )
    # if not signature_valid:
    #     raise ValueError("Invalid signature")
    
    # Code proceeds without verification
    return process_data(message)

Why it's vulnerable:
The signature verification logic is disabled, so any message—forged or tampered—is accepted as authentic.

Fixed pattern
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives import hashes
from cryptography.exceptions import InvalidSignature

def process_signed_message(message, signature, public_key):
    try:
        public_key.verify(
            signature,
            message,
            padding.PSS(padding.MGF1(hashes.SHA256()), padding.PSS.MAX_LENGTH),
            hashes.SHA256()
        )
    except InvalidSignature:
        raise ValueError("Signature verification failed")
    
    return process_data(message)
Vulnerable pattern
<?php
// Vulnerable: signature verification uses loose comparison
function verify_signed_data($data, $signature, $public_key_pem) {
    $public_key = openssl_pkey_get_public($public_key_pem);
    $result = openssl_verify($data, $signature, $public_key, OPENSSL_ALGO_SHA256);
    
    // Loose comparison: treats 0 (failure) and false (error) the same
    if ($result == 1) {
        return true;
    }
    return false;
}

$is_valid = verify_signed_data($user_data, $user_signature, $trusted_key);
if ($is_valid) {
    process_data($user_data);
}
?>

Why it's vulnerable:
The comparison $result == 1 uses loose equality, which can cause unexpected type coercion. More critically, the function doesn't distinguish between verification failure and cryptographic error.

Fixed pattern
<?php
function verify_signed_data($data, $signature, $public_key_pem) {
    $public_key = openssl_pkey_get_public($public_key_pem);
    if ($public_key === false) {
        throw new Exception("Invalid public key");
    }
    
    $result = openssl_verify($data, $signature, $public_key, OPENSSL_ALGO_SHA256);
    
    // Strict comparison: only accept 1 (valid signature)
    if ($result === 1) {
        return true;
    } elseif ($result === 0) {
        throw new Exception("Signature verification failed");
    } else {
        throw new Exception("Signature verification error");
    }
}

try {
    if (verify_signed_data($user_data, $user_signature, $trusted_key)) {
        process_data($user_data);
    }
} catch (Exception $e) {
    log_error($e->getMessage());
}
?>

05Prevention Checklist

Always call the cryptographic verification function; never comment it out or skip it in production code.
Use strict equality (=== in PHP, is in Python) when checking verification results; never rely on loose comparison.
Verify the entire signature and data, not just a portion or a hash of it.
Use well-maintained cryptographic libraries (OpenSSL, cryptography module, libsodium) rather than implementing signature logic yourself.
Ensure the public key used for verification comes from a trusted, authenticated source and is not user-supplied.
Test signature verification with both valid and invalid signatures to confirm the code rejects forged data.

06Signs You May Already Be Affected

Look for disabled or commented-out verification code in your codebase, especially around cryptographic operations. Check logs for unexpected data processing or authentication bypasses. If you've recently updated authentication tokens, signed packages, or message verification logic, audit those changes to confirm verification is active and correct.

07Related Recent Vulnerabilities