A hands-on cybersecurity demo for seminars & classrooms

Vulnerable

Early-exit comparison

def check_password(guess, secret):
    for i in range(len(secret)):
        if guess[i] != secret[i]:
            # exits immediately!
            return False
        # each match adds delay
        time.sleep(0.01)
    return True

Each correct character adds a measurable delay before the function exits. An attacker can probe position by position, picking the character that made the server respond slowest.

Secure

Constant-time comparison

import hmac

# Always compares every byte —
# no early exit, no timing leak.
result = hmac.compare_digest(
    guess, secret
)

Takes the same amount of time regardless of how many characters match. The attacker learns nothing from timing because the function always does the same work.

How the attack works

1
Probe position 0 Try every possible character (a–z, 0–9) and measure response time for each
2
Find the slowest The character that took longest matched — the server kept comparing longer
3
Repeat per position Lock in found characters, probe the next position. 36 guesses × 6 chars = 216 total
!
Password cracked Without ever brute-forcing all combinations (36⁶ = 2.1 billion)

Server configuration

●●●●●●
5ms (subtle)150ms (obvious)

Send a guess

Response log

Send some guesses above to see response times...
0
Requests sent
0 / 6
Chars found
0.0s
Time elapsed
Requests saved
_ _ _ _ _ _

Attack log

Press "Start timing attack" to begin...