|
| 1 | +# Random Statement |
| 2 | + |
| 3 | +:::{warning} |
| 4 | +The `random` module is **not** suitable for security or cryptographic purposes. Its pseudo-random number generator is deterministic and predictable if an attacker observes enough output. Never use it for generating session tokens, encryption keys, passwords, or any other security-sensitive values. |
| 5 | +::: |
| 6 | + |
| 7 | +## Security Concerns |
| 8 | + |
| 9 | +The `random` module in Python is unsafe for security or cryptographic purposes, such as generating session tokens, encryption keys, or passwords. |
| 10 | + |
| 11 | +This is because the `random` module uses a pseudo-random number generator (PRNG) called the Mersenne Twister. This algorithm is **deterministic**—if an attacker can observe a sufficient amount of its output, they can completely determine its internal state (the seed) and accurately predict all future and even past values. This predictability can lead to severe vulnerabilities, including session hijacking, credential compromise, and unauthorised access. |
| 12 | + |
| 13 | +The following `random` functions can introduce weaknesses when used in security contexts: |
| 14 | +- `random.seed` |
| 15 | +- `random.Random` |
| 16 | +- `random.randbytes` |
| 17 | +- `random.randint` |
| 18 | +- `random.random` |
| 19 | +- `random.randrange` |
| 20 | +- `random.triangular` |
| 21 | +- `random.uniform` |
| 22 | + |
| 23 | +The `random` module is specifically designed for non-security-sensitive applications like simulations, statistical modelling, and simple games, prioritising speed and good statistical distribution over true unpredictability. |
| 24 | + |
| 25 | +For all security-sensitive tasks, you **must** use the `secrets` module, which relies on a Cryptographically Secure Pseudo-Random Number Generator (CSPRNG) provided by the operating system. |
| 26 | + |
| 27 | +## Preventive Measures |
| 28 | + |
| 29 | +To avoid introducing predictable randomness vulnerabilities in your applications, follow these guidelines: |
| 30 | + |
| 31 | +- **Use `secrets` for security purposes:** For any security-sensitive randomness—such as passwords, tokens, or cryptographic keys—always use the `secrets` module. This module provides functions like `secrets.token_bytes()`, `secrets.token_hex()`, and `secrets.choice()` that are designed for cryptographic security. |
| 32 | + |
| 33 | ++++ |
| 34 | + |
| 35 | + |
| 36 | +- **Use `random.SystemRandom` with caution:** The `SystemRandom` class uses `os.urandom()` and can be used as a drop-in replacement for the `random` module's functions. However, it may not be available on all systems, and using `secrets` directly is the preferred, more explicit approach. |
| 37 | + |
| 38 | ++++ |
| 39 | + |
| 40 | +- **Review third-party dependencies:** Ensure that any third-party libraries you use are not relying on the `random` module for security-critical operations. Vulnerabilities can be introduced through transitive dependencies. |
| 41 | + |
| 42 | + |
| 43 | +## Example |
| 44 | + |
| 45 | +Consider the following problematic code that uses `random` to generate a browser cookie: |
| 46 | + |
| 47 | +```python |
| 48 | +"""Problematic code using random module""" |
| 49 | +import random |
| 50 | + |
| 51 | +browser_cookie = random.randint(min_value, max_value) |
| 52 | +``` |
| 53 | + |
| 54 | + |
| 55 | +This code is vulnerable because an attacker could predict the cookie value if they observe enough outputs from the same PRNG instance. |
| 56 | + |
| 57 | +To improve this code, use the `secrets` module: |
| 58 | + |
| 59 | + |
| 60 | +Use the `SystemRandom` class. This class uses the system function `os.urandom()` to generate random numbers from sources provided by the operating system. |
| 61 | + |
| 62 | +```python |
| 63 | +from random import SystemRandom |
| 64 | +safe_random = SystemRandom() |
| 65 | + |
| 66 | +browser_cookie = safe_random.randint(min_value, max_value) |
| 67 | +``` |
| 68 | + |
| 69 | +Alternatively, if you need a drop-in replacement for `random` functions, use `SystemRandom`: |
| 70 | + |
| 71 | +```python |
| 72 | +from random import SystemRandom |
| 73 | +safe_random = SystemRandom() |
| 74 | + |
| 75 | +browser_cookie = safe_random.randint(min_value, max_value) |
| 76 | +``` |
| 77 | + |
| 78 | +However, note that `secrets` is the recommended approach for all new code requiring cryptographic randomness. |
| 79 | + |
| 80 | + |
| 81 | +## Discussion |
| 82 | + |
| 83 | +The root cause of the security issue lies in the design goals of the Mersenne Twister algorithm. It was developed for speed and statistical quality in simulations, not for unpredictability. Its state can be fully recovered from just 624 consecutive outputs, making it trivial for an attacker to predict future values once they have observed enough random numbers. |
| 84 | + |
| 85 | +The Python standard library provides the `secrets` module specifically to address this gap. Introduced in Python 3.6 via [PEP 506](https://peps.python.org/pep-0506/), `secrets` was designed to be the "go-to" module for all cryptographic randomness needs. It abstracts away platform-specific CSPRNG implementations and provides a simple, safe API for developers. |
| 86 | + |
| 87 | +A common misconception is that using `random.seed()` with a high-entropy seed makes the `random` module secure. This is false—while it may make prediction more difficult initially, the Mersenne Twister's state can still be recovered from its outputs, regardless of the seed's quality. |
| 88 | + |
| 89 | +## More Information |
| 90 | + |
| 91 | +* https://docs.python.org/3/library/random.html |
| 92 | +* https://docs.python.org/3/library/secrets.html#module-secrets |
| 93 | +* [ CWE-330: Use of Insufficiently Random Values](https://cwe.mitre.org/data/definitions/330.html) |
| 94 | +* [CWE-338: Use of Cryptographically Weak Pseudo-Random Number Generator (PRNG)](https://cwe.mitre.org/data/definitions/338.html) |
| 95 | +* [CVE-2022-23472](https://nvd.nist.gov/vuln/detail/CVE-2022-23472) |
| 96 | +* [PEP 506 – Adding A Secrets Module To The Standard Library](https://peps.python.org/pep-0506/) |
| 97 | +* https://www.codiga.io/blog/python-avoid-random/ |
| 98 | + |
0 commit comments