How to Implement a CSRF Token Double Submit Mock in Python
A mock CSRF protection class that generates and validates double-submit tokens using HMAC-SHA256 with a secret key.
Python code
33 linesimport hmac
import hashlib
import secrets
class CSRFProtection:
def __init__(self, secret_key: str):
self.secret_key = secret_key.encode("utf-8")
def generate_token(self) -> str:
random_value = secrets.token_hex(16)
signature = hmac.new(
self.secret_key, random_value.encode("utf-8"), hashlib.sha256
).hexdigest()
return f"{random_value}.{signature}"
def validate_token(self, token: str, submitted_token: str) -> bool:
if token != submitted_token:
return False
random_value, signature = token.split(".")
expected_signature = hmac.new(
self.secret_key, random_value.encode("utf-8"), hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, expected_signature)
if __name__ == "__main__":
csrf = CSRFProtection("my-super-secret-key")
stored_token = csrf.generate_token()
print(f"Stored token: {stored_token}")
print(f"Valid submission: {csrf.validate_token(stored_token, stored_token)}")
fake_token = csrf.generate_token()
print(f"Tampered submission: {csrf.validate_token(stored_token, fake_token)}")
Output
Stored token: 2f5a8b7c4d9e1f3a6b8c0d2e4f6a8b0c.a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2
Valid submission: True
Tampered submission: False
How it works
The generate_token method creates a random hex value and signs it with HMAC-SHA256 using a secret key. The token format is random_value.signature. During validation, the submitted token must exactly match the stored token, and then the signature is recomputed and compared using hmac.compare_digest to prevent timing attacks. This double-submit approach ensures that only someone with the secret key can forge or modify tokens. The secrets module is used for cryptographically secure random generation.
Common mistakes
- Comparing signatures with `==` instead of `hmac.compare_digest`
- Storing the secret key in plaintext in source code
- Not checking that the token format is correct before splitting
- Using a weak random generator like `random` for token generation
Variations
- Use a cookie-based double-submit where the same token is set in a cookie and a hidden form field
- Use signed tokens with a timestamp to enforce expiration
Real-world use cases
- Protecting web forms in a Flask or Django app from cross-site request forgery attacks.
- Securing AJAX POST endpoints where a CSRF token is sent in both a header and a form field.
- Implementing double-submit token validation in a microservice gateway to guard state-changing APIs.
Sponsored
More from Auth & security at scale
- ACME LetsEncrypt Mock Challenge Server in Python medium
- AES GCM encryption and decryption in Python medium
- Build a Mock OIDC Userinfo Endpoint in Python with Flask easy
- ChaCha20-Poly1305 mock in Python medium
- ECDH key agreement in Python with cryptography medium
- Enforce TLS 1.2 Minimum in Python easy
Keep learning
Related tutorials and quizzes for this topic.