Timing Attacks
debt(d5/e1/b3/t7)
Closest to 'specialist tool catches it' (d5). The detection_hints list semgrep and phpstan as the tools that catch the pattern of == or === comparing hash_hmac() outputs or token strings. These are not default linters but specialist SAST tools that require deliberate configuration, placing this squarely at d5.
Closest to 'one-line patch or single-call swap' (e1). The quick_fix is explicit: replace == or strcmp() with hash_equals($known_good, $user_supplied). This is a single-call swap at each comparison site, so e1 is the correct anchor, though multiple call sites may exist independently.
Closest to 'localised tax' (b3). The fix applies at each token/HMAC comparison point rather than spreading across the whole architecture. It is a recurring tax wherever secrets are compared, but each site is self-contained and the rest of the codebase is unaffected, keeping burden at b3.
Closest to 'serious trap' (t7). The misconception field states that developers widely believe timing attacks are theoretical or impractical, but in reality same-datacenter or cloud-colocated attackers can exploit sub-millisecond differences with enough samples. Additionally, the common_mistakes show that even hash_equals() has a subtlety around length mismatches that can still leak information, contradicting the assumption that switching to hash_equals() is fully sufficient without normalising lengths. This combination of 'looks safe but isn't' plus 'the obvious replacement also has a gotcha' lands at t7.
Also Known As
TL;DR
Explanation
A timing attack exploits the fact that standard string comparison (== or strcmp) returns early as soon as a mismatch is found — comparing 'abc' against 'xyz' takes less time than comparing 'abc' against 'abd'. An attacker who can make many requests and measure response times can recover a secret (HMAC token, API key, password reset token) one character at a time by submitting guesses and choosing the one that takes longest. PHP's hash_equals() performs constant-time comparison — it always inspects every byte regardless of where the first difference occurs, eliminating the timing signal. password_verify() is already constant-time. The attack is especially realistic for HMAC signature verification, API key comparison, and password reset token validation where the secret is a fixed-length string. Network jitter limits practical exploitation over the internet, but server-local or same-datacenter attacks are very feasible.
Watch Out
Common Misconception
Why It Matters
Common Mistakes
- Using === or == to compare HMAC signatures or API tokens — both short-circuit and leak timing information.
- Using strcmp() for security-sensitive comparisons — also non-constant-time.
- Comparing tokens of different lengths with hash_equals() — the function returns false immediately for length mismatches in some implementations; normalise length before comparing or use a fixed-length hash of both sides.
Code Examples
// Vulnerable — short-circuits on first mismatch:
if ($request->header('X-Signature') === hash_hmac('sha256', $payload, $secret)) {
processWebhook($payload);
}
// Constant-time comparison:
$expected = hash_hmac('sha256', $payload, $secret);
$received = $request->header('X-Signature') ?? '';
if (!hash_equals($expected, $received)) {
throw new InvalidSignatureException();
}
processWebhook($payload);