packages/skills/skills/security-constant-time-analysis/SKILL.md
Non-constant-time string comparison reveals information bit by bit:
# VULNERABLE: Early exit leaks prefix length
def check_token(provided, expected):
if len(provided) != len(expected):
return False
for a, b in zip(provided, expected):
if a != b:
return False # Exits early - timing reveals match position
return True
An attacker can measure response times to determine how many characters match, then brute-force one character at a time.
import hmac
# SAFE: constant-time comparison
hmac.compare_digest(provided_token, expected_token)
# Also safe for bytes
hmac.compare_digest(provided_hash, expected_hash)
const crypto = require('crypto');
// SAFE: constant-time comparison
crypto.timingSafeEqual(
Buffer.from(provided),
Buffer.from(expected)
);
import "crypto/subtle"
// SAFE: constant-time comparison
subtle.ConstantTimeCompare([]byte(provided), []byte(expected))
use subtle::ConstantTimeEq;
// SAFE: constant-time comparison
provided.ct_eq(&expected).into()
# Find potentially unsafe secret comparisons
grep -rn "==.*token\|==.*secret\|==.*password\|==.*api_key" --include="*.py" --include="*.js" --include="*.ts" .
# Find safe comparison usage
grep -rn "compare_digest\|timingSafeEqual\|ConstantTimeCompare\|ct_eq" .
| Pattern | Language | Risk |
|---|---|---|
if token == expected: | Python | Timing leak |
if (token === expected) | JavaScript | Timing leak |
strings.Compare(a, b) | Go | Timing leak |
token.equals(expected) | Java | Timing leak |
bcrypt.compare(a, b) | Any | Safe (bcrypt is constant-time) |
Timing leaks can also occur in: