TL;DR
The method you’re likely using (generating tokens with predictable sequences or weak randomness) is probably insecure. This guide shows how to create genuinely single-use tokens that are much harder to guess and prevent reuse.
Understanding the Risks
If your tokens aren’t strong enough, attackers can:
- Guess them: If they know (or can figure out) how you generate tokens, they can create valid ones.
- Reuse them: If a token isn’t properly invalidated after use, it can be used multiple times.
This leads to serious cyber security problems like unauthorised access and data breaches.
How to Generate Secure Single-Use Tokens
- Choose a Cryptographically Secure Random Number Generator (CSPRNG): Don’t use standard
rand()functions. These aren’t designed for security.- Python: Use the
secretsmodule. - PHP: Use
random_bytes()oropenssl_random_pseudo_bytes(). - JavaScript (Node.js): Use the
cryptomodule.
- Python: Use the
- Generate a Sufficiently Long Token: Longer tokens are harder to guess.
- Aim for at least 32 bytes (256 bits) of random data. This translates to roughly 58 hexadecimal characters.
- Encode the Token: Use a safe encoding like Base64 or hexadecimal.
- Base64 is more compact, but hexadecimal can be easier to debug.
- Python example (Base64):
import secrets import base64 token_bytes = secrets.token_bytes(32) token_string = base64.b64encode(token_bytes).decode('utf-8') print(token_string)
- Store Tokens Securely:
- Database: Use a properly configured database with appropriate access controls. Hash the token before storing it (see step 6).
- Avoid storing tokens in plain text!
- Implement Token Validation and Invalidation: This is crucial.
- When a token is used, immediately mark it as invalid. A simple way is to add a ‘used’ flag to the database record.
- Consider using a short Time-To-Live (TTL) for tokens even if they haven’t been used – this adds an extra layer of security.
- Hash Tokens Before Storing: Even with other precautions, hashing provides an additional level of protection.
- Use a strong hashing algorithm like SHA-256 or Argon2.
import hashlib hash_object = hashlib.sha256(token_string.encode('utf-8')) hashed_token = hash_object.hexdigest() print(hashed_token)
- Use a strong hashing algorithm like SHA-256 or Argon2.
- Prevent Token Reuse:
- Before accepting a token, check if it exists in the database and is marked as unused.
- If used, reject the request immediately.
Example Workflow
- User requests access.
- Generate a new secure token (steps 1-3).
- Hash the token (step 6).
- Store the hashed token in the database with a ‘used’ flag set to false.
- Send the token to the user.
- When the user submits the token:
- Check if the submitted token’s hash exists in the database.
- If it exists, check if the ‘used’ flag is false.
- If both conditions are true:
- Mark the ‘used’ flag as true.
- Grant access.
- Otherwise, reject the request.

