Skip to content

Services, Wiki-Artikel, Blog-Beiträge und Glossar-Einträge durchsuchen

↑↓NavigierenEnterÖffnenESCSchließen
Kryptographie & Protokolle Glossary

Timing Attack - Zeitbasierter Seitenangriff

Timing attacks exploit differences in the execution time of cryptographic operations or comparison functions to infer confidential information. Classic attacks: password timing (early termination upon the first difference), RSA timing (modular exponentiation), cache timing (Spectre/Meltdown). Protection: Constant-time comparisons (hmac.compare_digest), blinding, branch-free implementations.

Timing attacks belong to the class of side-channel attacks: Attackers do not exploit logical weaknesses in an algorithm, but rather physical or temporal properties of its implementation. Even a cryptographically sound algorithm can become vulnerable due to an insecure implementation if the execution time reveals secrets.

The Fundamental Problem

Insecure String Comparison Implementation (Conceptual)

Function: verify_token(user_input, secret)
  for i in range(len(user_input)):
    if user_input[i] != secret[i]:
      return False   ← EARLY TERMINATION!
  return True

Timing Leak

  • "AAAA..." → terminates after position 1 → very fast
  • "XBBB..." → terminates after position 1 → just as fast
  • "XAAA..." → terminates after position 2 → slightly slower
  • "XYAA..." → terminates after position 2 → just as fast

Measurement: Which first character is the correct one? → The one with the longest response time! Try each byte individually instead of all combinations!

  • Without timing: O(2^n) combinations for an n-bit key
  • With timing: O(n * 256) attempts for an n-byte key
  • → Reduced from exponential to linear!

Where does timing variance come from?

  1. Early termination during comparisons (most common cause)
  2. Branch prediction: if/else depending on the secret
  3. Cache hits: data in the CPU cache → faster
  4. Multiplication vs. exponentiation: different cycles
  5. Garbage collection: GC pauses can also mask it!
  6. Network jitter: makes remote timing more difficult

Attack scenarios

Scenario 1 - Web token validation (most common web vulnerability)

Attacker guesses HMAC token byte by byte through timing measurement.

Method:

  1. Thousands of requests with first byte = 'A'
  2. Thousands of requests with first byte = 'B'
  3. ...until the byte with the longest response time is found → first byte correct!
  4. Continue with the second byte...

Requirements:

  • Measurable response times (latency < noise)
  • Server without rate limiting
  • Many measurements required (statistical analysis)

Remote timing: more difficult (network jitter), but possible! Cloudflare research: microsecond differences across tens of thousands of requests are statistically measurable (t-test).

Scenario 2 - Username Enumeration

  • Valid username + incorrect password: 200 ms (password hash calculated!)
  • Invalid username: 5 ms (aborted early, no hash)

Attacker: Username enumeration via timing difference—even if the HTTP response looks identical.

> Proof: Amazon AWS SDK had this bug (CVE-2015-3154)

Scenario 3 - RSA Private Key (Kocher 1996, historical)

RSA decryption: Modular exponentiation using private exponents. Square-and-Multiply algorithm:

  • Bit = 1: Square + Multiply (more operations!)
  • Bit = 0: Square only (fewer operations)

Timing difference → Private key bits can be extrapolated! Affected early implementations without blinding.

Protection: Montgomery blinding (random blind factor before decryption)

Scenario 4 - Cache Timing (Spectre, 2018)

CPU cache stores recently accessed data. Attacker process measures access time to its own array elements. If access time is fast → data still in cache → CPU accessed this address during a previous operation!

Spectre tricks CPU branch prediction → speculative access to kernel memory → timing side channel → read data!

Affects: practically all modern CPUs (hardware problem!)

Mitigation: Kernel Page Table Isolation (KPTI), Retpoline, microcode updates (performance overhead!)

Scenario 5 - TOTP bypass via timing

TOTP validation without constant time:

  • Attacker sends 000000, measures response time
  • If "00" matches → slightly longer
  • Iterates through all 6 digits sequentially

Practically limited by: short TOTP validity (30s), network jitter, modern CT implementations.

Constant-Time Implementations

Python - WRONG vs. RIGHT

# WRONG: string == string → early termination possible!
if user_token == secret_token:
    grant_access()

# RIGHT: hmac.compare_digest - constant time!
import hmac
if hmac.compare_digest(user_token.encode(), secret_token.encode()):
    grant_access()
# Same execution time regardless of whether tokens match or not!

# ALSO CORRECT: secrets.compare_digest (Python 3.3+):
import secrets
if secrets.compare_digest(user_token, secret_token):
    grant_access()

Node.js - WRONG vs. RIGHT

// WRONG:
if (userToken === secretToken) { ... }
if (userToken == secretToken) { ... }

// CORRECT: crypto.timingSafeEqual (Node.js built-in):
const crypto = require(&#x27;crypto&#x27;);
const a = Buffer.from(userToken);
const b = Buffer.from(secretToken);

// IMPORTANT: Lengths must be equal for timingSafeEqual!
if (a.length === b.length &amp;&amp; crypto.timingSafeEqual(a, b)) {
    grantAccess();
}
// Compare lengths separately → does not reveal length information,
// since constant-time-equal returns &#x27;false&#x27; anyway if lengths differ

Java - WRONG vs. RIGHT

// WRONG:
if (userToken.equals(secretToken)) { ... }

// CORRECT: MessageDigest.isEqual (constant-time):
import java.security.MessageDigest;
if (MessageDigest.isEqual(
    userToken.getBytes(),
    secretToken.getBytes())) { ... }

Go - WRONG vs. RIGHT

// WRONG:
if userToken == secretToken { ... }

// RIGHT: subtle.ConstantTimeCompare:
import &quot;crypto/subtle&quot;
if subtle.ConstantTimeCompare([]byte(userToken), []byte(secretToken)) == 1 {
    // access granted
}

PHP - WRONG vs. RIGHT

// WRONG: strcmp(), ==, ===
// RIGHT: hash_equals() (PHP 5.6+)
if (hash_equals($secretToken, $userToken)) { ... }

Timing Tests in Penetration Testing

1. Username Enumeration (Timing)

# Tool: ffuf, Burp Intruder with timing analysis
for user in wordlist:
  time curl -s -X POST /login -d &quot;user=$user&amp;pass;=WRONG&quot;
# → Mean value per username + standard deviation
# → Outlier on the high end → valid username!

2. Token Comparison (Remote Timing)

# Tool: timing-attack-framework or custom script
for prefix in range(256):
  times = []
  for _ in range(100):  # many measurements!
    t = measure(POST /verify, token=chr(prefix)+garbage)
    times.append(t)
  results[prefix] = median(times)
# → Byte with highest median → likely correct

3. Statistical Analysis

  • Single measurement: too much noise!
  • Median over 50–100 measurements
  • Welch’s t-test: Is the difference statistically significant?
  • Tools: timing-attack (Python), bcheck (Burp)

4. Local Timing Tests (for Code Reviews)

# Python timeit: nanosecond resolution
# Find == vs hmac.compare_digest in codebase:
grep -r &quot;== request\|== token\|== key\|== secret&quot; src/

5. What to test

  • Login endpoints (username enumeration)
  • Token validation (API keys, CSRF tokens, OTP)
  • Password reset token verification
  • Authorization checks using string comparison
  • Crypto libraries (HMAC comparison)

Mitigation measures

1. Use constant-time functions everywhere

  • ONLY use constant-time comparisons for secrets: HMAC, tokens, passwords, keys
  • hmac.compare_digest (Python), timingSafeEqual (Node.js)
  • hash_equals (PHP), MessageDigest.isEqual (Java)
  • Code review checklist: no == for security-critical comparisons

2. Prevent username enumeration

  • Consistent response time during login (always compute the password hash!)
  • Compute a dummy hash even for invalid usernames
  • Same HTTP response for incorrect username AND incorrect password

3. Rate limiting

  • Makes timing attacks impractical (too many requests required)
  • Max 5–10 login attempts per minute per IP

4. Use network jitter (weaker mitigation)

  • Add a small random delay: time.sleep(random(0, 5ms))
  • Masks timing differences in the millisecond range
  • But: often still detectable with enough measurements!
  • Not a real solution, just makes it harder

5. Hardware-side (Spectre/Meltdown)

  • Install microcode updates
  • Kernel with KPTI, Retpoline
  • Browser: reduced timer resolution (SharedArrayBuffer disabled)
  • Accept performance trade-off

6. Cryptographic libraries

  • Use only verified cryptographic libraries (OpenSSL, libsodium, Bouncy Castle)
  • DO NOT implement your own cryptography!
  • RSA: Ensure padding blinding (OAEP instead of PKCS#1 v1.5)
  • AES-GCM: Avoid nonce reuse (GCM nonce misuse)