Insecure Randomness¶
What does this mean ?¶
Security-sensitive random values must be difficult for an attacker to predict. General-purpose generators intended for simulations, sampling or games do not necessarily provide this property. Timestamps, counters and user identifiers are also unsuitable substitutes for secret random tokens.
What can happen ?¶
Predictable reset or session tokens can permit unauthorized actions. A cryptographically strong generator can still produce an inadequate token if the output space is tiny: a number from 0 to 99 has only 100 possibilities, regardless of how it was selected. Online rate limits and expiry affect how guessing risk is controlled.
Recommendation¶
Use the operating system or language's cryptographic random API. Do not manually seed it with a timestamp, truncate it without a security rationale or fall back to a noncryptographic generator if it fails. Propagate generation failure and issue no token.
Choose entropy for the protocol. The examples use 32 random bytes for a high-entropy opaque token; a short human-entered one-time code has different requirements and needs strict throttling and a short lifetime. Use unbiased APIs when generating a random integer in a range rather than applying a potentially biased modulo operation.
Token generation is only one part of the control. Bind tokens to the intended purpose/account, expire and revoke them, make one-time tokens single-use, store a verification representation appropriate to the threat model, and avoid token logging. Base64 or hexadecimal encodes bytes for transport; encoding does not add entropy.
Sample Code¶
These examples generate a 256-bit random token and encode it. They do not send it, create a session or implement a password-reset workflow. Never print the token to an application log.
import secrets
def new_token() -> str:
return secrets.token_urlsafe(32)
random.random() and random.choice() are not replacements for this security use.
Node.js:
import { randomBytes } from 'node:crypto';
function newToken() {
return randomBytes(32).toString('base64url');
}
In a browser, use crypto.getRandomValues with a reviewed encoding step. Math.random() is not a security-token generator.
Modern .NET supports the static byte-array API:
static string NewToken()
{
byte[] bytes = System.Security.Cryptography.RandomNumberGenerator.GetBytes(32);
return Convert.ToHexString(bytes);
}
This produces 64 hexadecimal characters representing 32 random bytes. Do not call a static RandomNumberGenerator method through an instance or substitute System.Random.
static final java.security.SecureRandom TOKEN_RANDOM = new java.security.SecureRandom();
static String newToken() {
byte[] bytes = new byte[32];
TOKEN_RANDOM.nextBytes(bytes);
return java.util.Base64.getUrlEncoder().withoutPadding().encodeToString(bytes);
}
Let the provider seed the generator securely. Do not replace its seed with predictable application data.
function newToken(): string {
return bin2hex(random_bytes(32));
}
Let a randomness-generation exception stop the operation. rand and mt_rand are not suitable alternatives for this token.
func newToken() (string, error) {
bytes := make([]byte, 32)
if _, err := rand.Read(bytes); err != nil {
return "", err
}
return base64.RawURLEncoding.EncodeToString(bytes), nil
}
Imports: crypto/rand and encoding/base64. Handle any returned error without issuing a token. Current Go implementations may terminate on an underlying randomness-source failure; the example never falls back to math/rand.
require 'securerandom'
def new_token
SecureRandom.urlsafe_base64(32)
end
Use a supported Ruby/OpenSSL runtime and let generation failures propagate.
Regression checks¶
Verify decoding yields 32 bytes and the format is accepted by the consuming protocol. Generate a small fixture batch to catch accidental constant reuse, but do not treat a uniqueness test or statistical test as proof of cryptographic quality. With a stubbed generator, verify failure issues no token. Test expiry, single-use enforcement, purpose binding and log redaction in the application separately.