flawopen.com/Vulnerabilities/Timing Attacks & Constant-Time Comparison
Timing Attacks: Secret-Dependent Comparison and Defenses
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.
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
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.
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.
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.
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.
# 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
# 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
- 1Reachable comparison: An endpoint compares attacker-influenced candidate data with a secret-dependent value.
- 2Variable work: The implementation’s work may depend on secret content, such as an early mismatch.
- 3Observable signal: Repeated measurements under suitable conditions distinguish the signal from system and network noise.
- 4Conditional impact: Only if the signal reveals useful information about an authentication or integrity secret can it help an attacker; confirm the application-specific conditions.
Defensive Engineering & Prevention Rules
- ✓Use vetted comparison primitives: Python:
hmac.compare_digest(); Node.js:crypto.timingSafeEqual(); Go:subtle.ConstantTimeCompare(). Follow the documented input and length requirements. - ✓Compare canonical bytes: Parse and validate a fixed-format MAC or token before comparison. Use a fixed digest algorithm and expected size; reject malformed encodings consistently.
- ✓Do not rely on jitter: Random response delays are not a substitute for content-independent comparison and can often be averaged out.
- ✓Review the whole path: Check parsing, length/type errors, branches, logging, and response behavior. Node.js explicitly warns that
timingSafeEqualdoes not make surrounding code timing-safe. - ✓Test realistically: Add functional tests for valid and invalid signatures and assess side-channel risk in the target deployment; do not infer remote exploitability from a microbenchmark alone.