FlawOpen

flawopen.com/Vulnerabilities/Timing Attacks & Constant-Time Comparison

Timing Attacks: Secret-Dependent Comparison and Defenses

Context-dependent side-channel risk CWE-208 Cryptography & Auth
💡 Plain English Explainer — A Lock That Gives Clues

Imagine a lock that gives a slightly different response depending on how much of a guessed code matches. If an attacker can observe that difference reliably and repeat the measurement under suitable conditions, the response may reveal information about a secret. A comparison routine that exits based on secret content can create such a timing signal. Real network timing also includes noise from scheduling, queues, caches, and other work, so a short-circuit comparison does not by itself prove that a remote attacker can recover a secret.

Target: Comparisons involving secret values such as externally supplied HMACs, authentication cookies, or capability tokens.
Signal: Execution time that varies with secret-dependent work and is observable at a useful resolution.
Conditions: The secret-dependent signal must survive implementation and system noise; impact depends on repeatability, attacker access, and the value being compared.
Remediation: Use a vetted constant-time comparison for same-type, fixed-format secret values and review surrounding code for other timing leaks.

Core Concepts & Subsystem Terms

Timing side channel
Information inferred from execution-time differences that depend on secret data. Whether a difference is observable remotely depends on the system and measurement conditions.
Content-dependent comparison
A comparison implementation may stop or do different work based on the first mismatch. Language operators do not provide a general constant-time guarantee for secrets.
Constant-time comparison
A vetted primitive aims to make comparison work independent of the compared contents for the supported input shape. It can still depend on length or type, and surrounding code may leak information.
CWE-208
Observable Timing Discrepancy: execution time reveals information about an operation or data value.

Step-by-Step Attack Flow

Step 1

A secret is compared with attacker-influenced input

Examples include checking a webhook MAC, authentication cookie, or capability token. The attacker must be able to submit or influence candidates at a reachable comparison point.

Step 2

The implementation does secret-dependent work

A comparison that exits on a content mismatch can take different paths or amounts of work. The size and stability of any timing difference depend on the implementation and platform; there is no universal per-character delay.

Step 3

The timing signal must be observable

An attacker needs enough repeated observations and sufficiently controlled conditions to distinguish the signal from network, scheduler, cache, and application noise. A local timing difference does not automatically imply a practical remote attack.

Step 4

Impact depends on the secret and endpoint

If a distinguishable signal leaks information about a secret used for authentication or integrity, it may aid recovery or forgery. No secret is automatically reconstructed, and the impact must be established for the specific application.

The Mechanism & Root Cause

Do not assume a language’s ordinary equality operator is constant-time for secret values. Some implementations may stop at the first mismatch, but exact behavior is implementation-dependent, and remotely measurable leakage requires a stable signal and an attacker-controlled observation path. For HMAC verification, Python recommends hmac.compare_digest(); Node.js provides crypto.timingSafeEqual() for equal-length byte arrays; Go provides subtle.ConstantTimeCompare() for equal-length slices. Respect each API’s input-type and length requirements, and examine parsing, error handling, and surrounding branches too.

webhook_auth.py (Content-dependent pattern)Vulnerable
# RISKY: ordinary equality is not a constant-time guarantee for secrets.
def verify_webhook_signature(received, expected):
    # This result is correct functionally, but may reveal content-dependent work.
    return received == expected
webhook_auth.py (Fixed-length HMAC comparison)Hardened
# HARDENED: validate the public wire format, then compare equal-length bytes.
import hmac
import re

_HEX_SHA256 = re.compile(r"[0-9a-fA-F]{64}\Z")

def verify_webhook_signature(received_hex: str, expected_digest: bytes) -> bool:
    # expected_digest is the 32-byte HMAC-SHA-256 result.
    if len(expected_digest) != 32 or not _HEX_SHA256.fullmatch(received_hex):
        return False
    supplied_digest = bytes.fromhex(received_hex)
    return hmac.compare_digest(supplied_digest, expected_digest)

The Attack & Exploit Sequence

Defensive Engineering & Prevention Rules

Primary references and related security topics:Complete Security Directory →
CWE-208: Observable Timing Discrepancy → Python: hmac.compare_digest() → Node.js: crypto.timingSafeEqual() → Go: crypto/subtle.ConstantTimeCompare() → OWASP Web Security Testing Guide →