How to Build a Secure Random Number Generator in Python
Generating secure random numbers is crucial for cryptography, secure tokens, session IDs, passwords, and anywhere unpredictability matters. This guide shows how to build a secure random number generator (RNG) in Python, covering cryptographic vs non-cryptographic RNGs, best practices, practical examples, and testing.
1. Understand RNG types and when to use them
- Cryptographic RNG (CSPRNG): Suitable for keys, tokens, salts, passwords. Must be unpredictable and resist state compromise.
- Non-cryptographic RNG: Fast but predictable (e.g., for simulations, games). Do not use for security-sensitive tasks.
Use CSPRNG for anything that affects security or privacy.
2. Use Python’s built-in cryptographic sources
- secrets module: High-level API for generating secure tokens and random numbers.
- os.urandom(): Returns cryptographically strong random bytes from the OS.
- random.SystemRandom: random API backed by system CSPRNG.
Prefer the secrets module for new code.
3. Practical examples
Generate secure random bytes
python
import os # 32 cryptographically secure random bytes randbytes = os.urandom(32)
Generate a secure random integer in a range
python
import secrets # Secure random integer in range [0, 232 - 1] rand_int = secrets.randbits(32) # Secure random integer in inclusive range [a, b] a, b = 1, 100 securerand = secrets.randbelow(b - a + 1) + a
Generate secure tokens and passwords
python
import secrets import string # URL-safe token (default 32 bytes -> 43-character string) token = secrets.token_urlsafe(32) # Human-readable secure password alphabet = string.ascii_letters + string.digits + string.punctuation password = “.join(secrets.choice(alphabet) for _ in range(16))
Use SystemRandom when random API needed
python
import random from random import SystemRandom sr = SystemRandom() print(sr.randrange(1, 101))
4. Building a reusable secure RNG class
python
import secrets import os class SecureRNG: def random_bytes(self, n: int) -> bytes: return os.urandom(n) def randbits(self, k: int) -> int: return secrets.randbits(k) def randbelow(self, upper: int) -> int: return secrets.randbelow(upper) def randint(self, a: int, b: int) -> int: return secrets.randbelow(b - a + 1) + a def token_urlsafe(self, nbytes: int = 32) -> str: return secrets.tokenurlsafe(nbytes)
5. Avoid common pitfalls
- Don’t use random.random() or other Mersenne Twister functions for security-critical tasks.
- Avoid lengths that are too short (e.g., tokens < 16 bytes) for security-sensitive secrets.
- Don’t roll your own cryptographic algorithms or RNGs unless you are an expert.
- Securely store and handle secrets in memory and when persisting (use OS key stores or encrypted vaults).
6. Testing randomness and correctness
- Functional tests: verify output ranges, lengths, formats.
- Statistical tests: use tools like Dieharder or NIST STS for deep analysis only when implementing new CSPRNGs — generally unnecessary when using OS-provided CSPRNGs.
- Entropy checking: ensure your environment provides enough entropy (modern OSes do).
7. Example: generating API keys for a web app
python
def generate_api_key(length_bytes=32): return secrets.token_urlsafe(length_bytes)
Store hashed API keys (use HMAC or a slow hash like bcrypt/scrypt/argon2) or store raw tokens only if you cannot store hashes (then ensure transport and storage are encrypted).
8. Deployment considerations
- Ensure the runtime environment (containers, VMs) provides a proper entropy source. For headless or embedded systems, configure an entropy daemon if needed.
- Keep libraries up to date to receive security fixes.
- Monitor for platform-specific RNG issues.
9. Summary
- Use Python’s secrets and os.urandom for cryptographic randomness.
- Avoid non-cryptographic RNGs for security.
- Follow best practices: sufficient length, safe storage, no custom algorithms, and validate environment entropy.
This gives a secure, practical foundation for building RNG functionality in Python for tokens, keys
Leave a Reply