How to Verify Passwords in Constant Time in Python

Use hmac.compare_digest to verify passwords in constant time, preventing timing attacks that could reveal password length or character positions.

Easy Python 3.9+ Aug 9, 2026 Auth & security at scale 16 views 0 copies

Python code

29 lines
Python 3.9+
import hmac
import time

# Mock of a constant-time password comparison (prevents timing attacks)
def verify_password(stored_password: str, supplied_password: str) -> bool:
    # hmac.compare_digest runs in constant time (for a given length)
    return hmac.compare_digest(stored_password.encode(), supplied_password.encode())

if __name__ == "__main__":
    correct_password = "super_secret_123"
    wrong_password = "super_secret_124"

    start = time.perf_counter()
    result1 = verify_password(correct_password, correct_password)
    end = time.perf_counter()
    print(f"Correct match: {result1} (took {end - start:.10f}s)")

    start = time.perf_counter()
    result2 = verify_password(correct_password, wrong_password)
    end = time.perf_counter()
    print(f"Wrong match: {result2} (took {end - start:.10f}s)")

    # Timing attack example: time differences should be negligible
    # due to constant-time comparison regardless of where mismatch occurs
    short_wrong = "super_secret_1"
    start = time.perf_counter()
    verify_password(correct_password, short_wrong)
    end = time.perf_counter()
    print(f"Short wrong: (took {end - start:.10f}s)")

Output

stdout
Correct match: True (took 0.0000034000s)
Wrong match: False (took 0.0000035000s)
Short wrong: (took 0.0000031000s)

How it works

The hmac.compare_digest function performs a constant-time comparison of two byte strings, meaning the time taken does not depend on where the first mismatch occurs. This prevents attackers from using timing information to guess password characters incrementally. Both arguments are encoded to bytes because compare_digest requires bytes-like objects. The function returns a boolean indicating whether the passwords match. By timing comparisons, you can observe that correct, wrong, and short passwords take roughly the same time to verify, highlighting the security benefit.

Common mistakes

  • Using `==` for password comparison, which short-circuits on the first mismatch and leaks timing information
  • Forgetting to encode strings before calling `hmac.compare_digest`
  • Storing plaintext passwords instead of hashes — constant-time comparison alone is not sufficient for security

Variations

  1. Use `secrets.compare_digest` as an alternative to `hmac.compare_digest` for the same constant-time guarantee
  2. Compare hashed passwords (e.g., SHA-256) using `hmac.compare_digest` to avoid timing leaks while storing only hashes

Real-world use cases

  • Authenticating users in a login endpoint where timing side-channels could leak password information.
  • Verifying API tokens or session secrets before granting access to protected resources.
  • Checking HMAC signatures in webhook receivers to ensure requests are authentic without exposing timing details.

Sponsored

Run this sample

Open the browser IDE to tweak the example and see results without installing anything.

Open editor

More from Auth & security at scale

Related tutorials and quizzes for this topic.