Use of cryptographically weak pseudo-random number generator (PRNG)
ID |
dart.cryptography.insecure_randomness |
Severity |
high |
Remediation Complexity |
medium |
Remediation Risk |
medium |
Remediation Effort |
medium |
Resource |
Cryptography |
Language |
Dart |
Description
Dart’s default Random() is used to generate values in a security- sensitive context (key, token, nonce, salt, IV, session, secret). The default Random is a deterministic Mersenne-Twister-derived PRNG; its output is predictable to an attacker who recovers a few samples.
Rationale
The Dart Standard Library Random() constructor returns a non-secure
PRNG suitable for games, sampling, and reproducible randomness only.
Once an attacker observes a small number of outputs (often a single
long sample), they can recover the internal state and predict all
future outputs — and on most platforms, deterministically reproduce
past outputs as well.
Concrete consequences in a Flutter app:
-
Session IDs / CSRF tokens are guessable after the attacker logs in once and watches the bytes.
-
AES keys generated this way reduce 128 bits of entropy to ~32 bits of practical search space.
-
One-time-pad-style nonces collide quickly, breaking AEAD as in
non_random_iv_with_cbc_mode.
The rule narrows by variable name (token / password / secret / iv / nonce / salt / etc.) to avoid false positives on legitimate non- security uses such as game dice rolls or A/B-test bucketing.
The following code illustrates a vulnerable pattern detected by this rule:
class InsecureRandom {
void securityNamedVars() {
// VULNERABLE: Use of cryptographically weak pseudo-random number generator (PRNG)
final secret = Random();
// VULNERABLE: Use of cryptographically weak pseudo-random number generator (PRNG)
final keyRng = Random();
// VULNERABLE: Use of cryptographically weak pseudo-random number generator (PRNG)
final tokenRng = Random();
// VULNERABLE: Use of cryptographically weak pseudo-random number generator (PRNG)
final saltRng = Random();
// VULNERABLE: Use of cryptographically weak pseudo-random number generator (PRNG)
final passwordSeed = Random();
// VULNERABLE: Use of cryptographically weak pseudo-random number generator (PRNG)
final nonceRng = Random();
// VULNERABLE: Use of cryptographically weak pseudo-random number generator (PRNG)
final sessionId = Random();
// VULNERABLE: Use of cryptographically weak pseudo-random number generator (PRNG)
final uuidGen = Random();
// VULNERABLE: Use of cryptographically weak pseudo-random number generator (PRNG)
final cryptoRng = Random();
// VULNERABLE: Use of cryptographically weak pseudo-random number generator (PRNG)
final ivRng = Random();
print('$secret $keyRng $tokenRng $saltRng $passwordSeed $nonceRng $sessionId $uuidGen $cryptoRng $ivRng');
}
}
Remediation
Always call Random.secure() for security-sensitive random bytes — it
is backed by the OS CSPRNG (/dev/urandom, BCryptGenRandom,
SecRandomCopyBytes). For larger buffers, prefer the
package-provided helpers (IV.fromSecureRandom,
Key.fromSecureRandom, cipher.newNonce() in package:cryptography,
FortunaRandom seeded from Random.secure() in package:pointycastle).
// Vulnerable
final secret = Random();
final tokenRng = Random(42);
final keyBytes = List.generate(32, (_) => Random().nextInt(256));
// Fixed
final secret = Random.secure();
// Or package-provided helpers
final iv = IV.fromSecureRandom(16);
final key = Key.fromSecureRandom(32);
References
-
OWASP Top 10 2025 - A04 : Cryptographic Failures.
-
Random.secure() — Dart API docs — The Dart team’s canonical CSPRNG constructor
-
Talsec — OWASP Mobile Top 10 for Flutter M10 — Insufficient cryptography, with concrete weak-RNG examples