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);

Configuration

This detector does not need any configuration.

References

© 2026 Xygeni. All rights reserved