VNX-GO-008 – Go Weak PRNG for Security
Overview
This rule flags Go files that import math/rand. While math/rand is appropriate for non-security uses such as shuffling a playlist or randomizing test data, it is a pseudo-random number generator seeded deterministically and its output sequence can be predicted or reconstructed by an attacker. Any security-sensitive value — session tokens, password reset links, CSRF nonces, API keys, or one-time codes — that is generated with math/rand is potentially guessable. This maps to CWE-338: Use of Cryptographically Weak Pseudo-Random Number Generator (PRNG).
Severity: Medium | CWE: CWE-338 – Use of Cryptographically Weak Pseudo-Random Number Generator (PRNG)
Why This Matters
The output of math/rand is fully determined by its seed. In Go 1.20 and earlier, math/rand defaulted to a constant seed of 1 unless explicitly seeded — meaning the same sequence of numbers appeared in every process that forgot to call rand.Seed. Even with a time-based seed, an attacker who can observe a few generated values (or who knows approximately when the process started) can reconstruct the internal state and predict all future outputs. A session token with only 32 bits of effective entropy drawn from math/rand can be brute-forced in seconds. Token guessing attacks (MITRE ATT&CK T1110 – Brute Force) targeting predictable session identifiers or password reset tokens are well-documented and actively exploited.
What Gets Flagged
The rule fires on any .go file that imports the math/rand package. This is a conservative signal: the import does not by itself confirm that the random values are used for security, but it means the code should be reviewed to confirm crypto/rand is used wherever security matters.
// FLAGGED: math/rand imported; values from this package must not be used for security purposes
import (
"math/rand"
"fmt"
)
func generateToken() string {
// This token is predictable — do not use for authentication
return fmt.Sprintf("%d", rand.Int63())
}
Remediation
- Replace
math/randwithcrypto/randfor all security-sensitive random values. Thecrypto/randpackage reads from the operating system’s cryptographically secure entropy source (/dev/urandomon Linux,BCryptGenRandomon Windows).
import (
"crypto/rand"
"encoding/base64"
"fmt"
)
// SAFE: cryptographically secure random token
func generateToken() (string, error) {
b := make([]byte, 32) // 256 bits of entropy
if _, err := rand.Read(b); err != nil {
return "", fmt.Errorf("failed to generate token: %w", err)
}
return base64.URLEncoding.EncodeToString(b), nil
}
- Generate secure random integers using
crypto/randwithbig.Int. For cases where you need a random integer in a range:
import (
"crypto/rand"
"math/big"
)
// SAFE: cryptographically secure random integer in [0, max)
func secureRandInt(max int64) (int64, error) {
n, err := rand.Int(rand.Reader, big.NewInt(max))
if err != nil {
return 0, err
}
return n.Int64(), nil
}
Keep
math/randonly for non-security use cases. If your codebase legitimately usesmath/randfor things like test data generation, simulations, or non-security shuffles, ensure those uses are clearly separated from security functions. Add a comment to make the intent explicit and avoid accidental reuse for security purposes.Audit all token, key, and nonce generation in your codebase. Search for all call sites that produce values used in authentication, authorization, or cryptographic operations, and confirm each uses
crypto/rand.