Insecure Randomness

ID

go.insecure_randomness

Severity

high

Resource

Predictability

Language

Go

Tags

CWE:330, CWE:332, CWE:336, CWE:337, CWE:338, NIST.SP.800-53, OWASP:2021:A2, PCI-DSS:6.5.3

Description

Use of cryptographically weak pseudo-random number generator (PRNG).

Insecure randomness errors occur when a function that can produce predictable values is used as a source of randomness in a security-sensitive context.

Computers are unable to produce true randomness. Pseudo-Random Number Generators (PRNGs) approximate randomness algorithmically, starting with a seed from which subsequent values are calculated.

There are two types of PRNGs: statistical and cryptographic. Statistical PRNGs provide useful statistical properties, but their output is predictable and forms an easy to reproduce numeric stream, unsuitable for use in cases where security depends on generated values being unpredictable.

Cryptographic PRNGs address this problem by generating output that is more difficult to predict. For a value to be cryptographically secure, it must be highly improbable for an attacker to distinguish between it and a truly random value. In general, if a PRNG algorithm is not advertised as being cryptographically secure, then it is probably a statistical PRNG and should not be used in security-sensitive contexts, where its use can lead to serious vulnerabilities such as easy-to-guess temporary passwords, predictable cryptographic keys, session hijacking, and DNS spoofing.

Rationale

Randomness is often utilized in software applications for generating keys, tokens, session identifiers, and more. However, not all random number generators are suitable for security-sensitive tasks.

Example of insecure randomness:

package main

import (
	"fmt"
	"math/rand"
	"time"
)

func generateToken() string {
	// Seed the random number generator
	rand.Seed(time.Now().UnixNano())

	// Generate a predictable token: not suitable for security tokens
	token := fmt.Sprintf("%06d", rand.Intn(900000)+100000)
	return token
}

func main() {
	token := generateToken()
	fmt.Println("Generated token:", token)
}

If this kind of token is used for password recovery, login validation, or MFA, an attacker could potentially guess the token, especially if they know the time it was generated or have access to previous tokens. That leads to account takeover or unauthorized access.

Remediation

Always use cryptographically secure randomness for security-sensitive operations. In Golang, the crypto/rand module is designed for this purpose. Here are the recommended secure alternative:

package main

import (
	"crypto/rand"
	"fmt"
	"math/big"
)

func generateSecureToken() (string, error) {
	// Securely generate a random number between 100000 and 999999
	max := big.NewInt(900000)          // Upper bound (exclusive)
	n, err := rand.Int(rand.Reader, max)
	if err != nil {
		return "", err
	}

	token := fmt.Sprintf("%06d", n.Int64()+100000) // Ensure the number is six digits
	return token, nil
}

func main() {
	token, err := generateSecureToken()
	if err != nil {
		fmt.Println("Error generating token:", err)
		return
	}
	fmt.Println("Generated secure token:", token)
}

To address issues related to insecure randomness in Go:

  1. Identify Insecure Functions: Replace instances of the math/rand package used for security purposes with cryptographically secure alternatives.

  2. Use Cryptographically Secure Functions: Ensure all random numbers used for security purposes (such as tokens, keys, and nonces) are generated using the crypto/rand package.

  3. Audit Code Regularly: Regularly review the codebase to ensure that cryptographically secure functions are used where needed, especially in areas related to authentication, authorization, and sensitive data generation.

  4. Security Training: Educate developers on the importance of secure randomness and instruct them on using Go’s cryptographic packages correctly.

By implementing these guidelines, you can protect your Go applications from vulnerabilities due to predictable random number generation.

Configuration

The rule has the following configurable parameters:

  • checkSecurityContext, that indicates if the detector should raise issues that are not located under a security context. When this is set to false, the issues not located under a security context will still be reported but with INFO severity.

  • securityContextPattern, the pattern used to match the code units (like functions) that are related to a security context.

References

  • CWE-338 : Use of Cryptographically Weak Pseudo-Random Number Generator (PRNG).

  • CWE-330 : Use of Insufficiently Random Values

  • CWE-332 : Insufficient Entropy in PRNG

  • CWE-336 : Same Seed in Pseudo-Random Number Generator (PRNG)

  • CWE-337 : Predictable Seed in Pseudo-Random Number Generator (PRNG)

  • OWASP - Top 10 2021 - A2 : Cryptographic Failures