VYPR
Low severityNVD Advisory· Published Dec 27, 2022· Updated Apr 11, 2025

Insufficient randomness in github.com/Masterminds/goutils

CVE-2021-4238

Description

Randomly-generated alphanumeric strings contain significantly less entropy than expected. The RandomAlphaNumeric and CryptoRandomAlphaNumeric functions always return strings containing at least one digit from 0 to 9. This significantly reduces the amount of entropy in short strings generated by these functions.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

GoUtils' RandomAlphaNumeric and CryptoRandomAlphaNumeric always include a digit, reducing entropy and making short strings predictable.

Vulnerability

Description The vulnerability in the GoUtils library (CVE-2021-4238) affects the RandomAlphaNumeric and CryptoRandomAlphaNumeric functions. These functions are designed to generate random alphanumeric strings but contain a flaw: they always ensure that the generated string includes at least one digit from 0 to 9 [1]. This guarantee reduces the effective entropy of the output, especially for short strings, because the set of possible strings is smaller than what a truly random selection would produce.

Exploitation

An attacker can exploit this reduced entropy to more easily guess or brute-force tokens, passwords, or other secrets generated by these functions. The vulnerability does not require authentication; any system that relies on these functions for generating random strings (e.g., session tokens, recovery codes) is at risk. The attack surface includes applications that use GoUtils for security-sensitive randomness.

Impact

The impact is a significant reduction in security for any application depending on these functions for cryptographic or security purposes. Short strings become particularly predictable; for example, a 3-character string may only have a few hundred possibilities instead of tens of thousands. This could lead to account takeover, session hijacking, or bypassing of access controls.

Mitigation

The issue was fixed in commit f1923532a168b8203bfe956d8cd3b17ebece5982 [2]. The fix removes the logic that forces a digit into the string, relying solely on the underlying random generation. Users should update to a patched version (v1.1.1 or later) [4]. No workaround is available other than replacing the library with a secure random generator.

AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
github.com/Masterminds/goutilsGo
< 1.1.11.1.1

Affected products

5

Patches

2
f1923532a168

Merge pull request from GHSA-xg2h-wx96-xgxr

https://github.com/Masterminds/goutilsMatt ButcherFeb 4, 2021via ghsa
4 files changed · +76 45
  • cryptorandomstringutils.go+2 23 modified
    @@ -21,7 +21,6 @@ import (
     	"fmt"
     	"math"
     	"math/big"
    -	"regexp"
     	"unicode"
     )
     
    @@ -99,27 +98,7 @@ Returns:
     	error - an error stemming from an invalid parameter within underlying function, CryptoRandom(...)
     */
     func CryptoRandomAlphaNumeric(count int) (string, error) {
    -	if count == 0 {
    -		return "", nil
    -	}
    -	RandomString, err := CryptoRandom(count, 0, 0, true, true)
    -	if err != nil {
    -		return "", fmt.Errorf("Error: %s", err)
    -	}
    -	match, err := regexp.MatchString("([0-9]+)", RandomString)
    -	if err != nil {
    -		panic(err)
    -	}
    -
    -	if !match {
    -		//Get the position between 0 and the length of the string-1  to insert a random number
    -		position := getCryptoRandomInt(count)
    -		//Insert a random number between [0-9] in the position
    -		RandomString = RandomString[:position] + string('0' + getCryptoRandomInt(10)) + RandomString[position + 1:]
    -		return RandomString, err
    -	}
    -	return RandomString, err
    -
    +	return CryptoRandom(count, 0, 0, true, true)
     }
     
     /*
    @@ -204,7 +183,7 @@ func CryptoRandom(count int, start int, end int, letters bool, numbers bool, cha
     		if chars == nil {
     			ch = rune(getCryptoRandomInt(gap) + int64(start))
     		} else {
    -			ch = chars[getCryptoRandomInt(gap) + int64(start)]
    +			ch = chars[getCryptoRandomInt(gap)+int64(start)]
     		}
     
     		if letters && unicode.IsLetter(ch) || numbers && unicode.IsDigit(ch) || !letters && !numbers {
    
  • cryptorandomstringutils_test.go+36 0 modified
    @@ -1,6 +1,8 @@
     package goutils
     
     import (
    +	"regexp"
    +	"strconv"
     	"testing"
     	"unicode/utf8"
     )
    @@ -74,3 +76,37 @@ func TestCryptoRandomAlphaNumeric(t *testing.T) {
     		}
     	}
     }
    +
    +func TestCryptoRandAlphaNumeric_FuzzOnlyNumeric(t *testing.T) {
    +
    +	// Testing for a reported regression in which some versions produced
    +	// a predictably small set of chars.
    +	iters := 1000
    +	charlen := 0
    +	for i := 0; i < 16; i++ {
    +		numOnly := 0
    +		charlen++
    +		for i := 0; i < iters; i++ {
    +			out, err := CryptoRandomAlphaNumeric(charlen)
    +			if err != nil {
    +				t.Fatal("func failed to produce a random thinger")
    +			}
    +			if _, err := strconv.Atoi(out); err == nil {
    +				numOnly++
    +			}
    +
    +			m, err := regexp.MatchString("^[0-9a-zA-Z]+$", out)
    +			if err != nil {
    +				t.Fatal(err)
    +			}
    +			if !m {
    +				t.Fatal("Character is not alphanum")
    +			}
    +		}
    +
    +		if numOnly == iters {
    +			t.Fatalf("Got %d numeric-only random sequences", numOnly)
    +		}
    +	}
    +
    +}
    
  • randomstringutils.go+2 22 modified
    @@ -20,7 +20,6 @@ import (
     	"fmt"
     	"math"
     	"math/rand"
    -	"regexp"
     	"time"
     	"unicode"
     )
    @@ -75,12 +74,10 @@ func RandomNumeric(count int) (string, error) {
     
     /*
     RandomAlphabetic creates a random string whose length is the number of characters specified.
    -Characters will be chosen from the set of alpha-numeric characters as indicated by the arguments.
    +Characters will be chosen from the set of alphabetic characters.
     
     Parameters:
     	count - the length of random string to create
    -	letters - if true, generated string may include alphabetic characters
    -	numbers - if true, generated string may include numeric characters
     
     Returns:
     	string - the random string
    @@ -102,24 +99,7 @@ Returns:
     	error - an error stemming from an invalid parameter within underlying function, RandomSeed(...)
     */
     func RandomAlphaNumeric(count int) (string, error) {
    -	RandomString, err := Random(count, 0, 0, true, true)
    -	if err != nil {
    -		return "", fmt.Errorf("Error: %s", err)
    -	}
    -	match, err := regexp.MatchString("([0-9]+)", RandomString)
    -	if err != nil {
    -		panic(err)
    -	}
    -
    -	if !match {
    -		//Get the position between 0 and the length of the string-1  to insert a random number
    -		position := rand.Intn(count)
    -		//Insert a random number between [0-9] in the position
    -		RandomString = RandomString[:position] + string('0'+rand.Intn(10)) + RandomString[position+1:]
    -		return RandomString, err
    -	}
    -	return RandomString, err
    -
    +	return Random(count, 0, 0, true, true)
     }
     
     /*
    
  • randomstringutils_test.go+36 0 modified
    @@ -3,6 +3,8 @@ package goutils
     import (
     	"fmt"
     	"math/rand"
    +	"regexp"
    +	"strconv"
     	"testing"
     )
     
    @@ -76,3 +78,37 @@ func ExampleRandomSeed() {
     	// H_I;E
     	// 2b2ca
     }
    +
    +func TestRandAlphaNumeric_FuzzOnlyNumeric(t *testing.T) {
    +
    +	// Testing for a reported regression in which some versions produced
    +	// a predictably small set of chars.
    +	iters := 1000
    +	charlen := 0
    +	for i := 0; i < 16; i++ {
    +		numOnly := 0
    +		charlen++
    +		for i := 0; i < iters; i++ {
    +			out, err := RandomAlphaNumeric(charlen)
    +			if err != nil {
    +				t.Fatal("func failed to produce a random thinger")
    +			}
    +			if _, err := strconv.Atoi(out); err == nil {
    +				numOnly++
    +			}
    +
    +			m, err := regexp.MatchString("^[0-9a-zA-Z]+$", out)
    +			if err != nil {
    +				t.Fatal(err)
    +			}
    +			if !m {
    +				t.Fatal("Character is not alphanum")
    +			}
    +		}
    +
    +		if numOnly == iters {
    +			t.Fatalf("Got %d numeric-only random sequences", numOnly)
    +		}
    +	}
    +
    +}
    
869801f20f9f

Remove unnecessary checks on a value that is already definitely an alphanum

https://github.com/Masterminds/goutilsMatt ButcherJan 28, 2021via ghsa
4 files changed · +78 35
  • cryptorandomstringutils.go+2 19 modified
    @@ -21,7 +21,6 @@ import (
     	"fmt"
     	"math"
     	"math/big"
    -	"regexp"
     	"unicode"
     )
     
    @@ -102,24 +101,8 @@ func CryptoRandomAlphaNumeric(count int) (string, error) {
     	if count == 0 {
     		return "", nil
     	}
    -	RandomString, err := CryptoRandom(count, 0, 0, true, true)
    -	if err != nil {
    -		return "", fmt.Errorf("Error: %s", err)
    -	}
    -	match, err := regexp.MatchString("([0-9]+)", RandomString)
    -	if err != nil {
    -		panic(err)
    -	}
    -
    -	if !match {
    -		//Get the position between 0 and the length of the string-1  to insert a random number
    -		position := getCryptoRandomInt(count)
    -		//Insert a random number between [0-9] in the position
    -		RandomString = RandomString[:position] + string('0' + getCryptoRandomInt(10)) + RandomString[position + 1:]
    -		return RandomString, err
    -	}
    -	return RandomString, err
     
    +	return CryptoRandom(count, 0, 0, true, true)
     }
     
     /*
    @@ -204,7 +187,7 @@ func CryptoRandom(count int, start int, end int, letters bool, numbers bool, cha
     		if chars == nil {
     			ch = rune(getCryptoRandomInt(gap) + int64(start))
     		} else {
    -			ch = chars[getCryptoRandomInt(gap) + int64(start)]
    +			ch = chars[getCryptoRandomInt(gap)+int64(start)]
     		}
     
     		if letters && unicode.IsLetter(ch) || numbers && unicode.IsDigit(ch) || !letters && !numbers {
    
  • cryptorandomstringutils_test.go+37 0 modified
    @@ -1,6 +1,8 @@
     package goutils
     
     import (
    +	"regexp"
    +	"strconv"
     	"testing"
     	"unicode/utf8"
     )
    @@ -74,3 +76,38 @@ func TestCryptoRandomAlphaNumeric(t *testing.T) {
     		}
     	}
     }
    +
    +func TestCryptoRandAlphaNumeric_FuzzOnlyNumeric(t *testing.T) {
    +
    +	// Testing for a reported regression in which some versions produced
    +	// a predictably small set of chars.
    +	iters := 1000
    +	charlen := 0
    +	for i := 0; i < 16; i++ {
    +		numOnly := 0
    +		charlen++
    +		for i := 0; i < iters; i++ {
    +			out, err := CryptoRandomAlphaNumeric(charlen)
    +			println(out)
    +			if err != nil {
    +				t.Fatal("func failed to produce a random thinger")
    +			}
    +			if _, err := strconv.Atoi(out); err == nil {
    +				numOnly++
    +			}
    +
    +			m, err := regexp.MatchString("^[0-9a-zA-Z]+$", out)
    +			if err != nil {
    +				t.Fatal(err)
    +			}
    +			if !m {
    +				t.Fatal("Character is not alphanum")
    +			}
    +		}
    +
    +		if numOnly == iters {
    +			t.Fatalf("Got %d numeric-only random sequences", numOnly)
    +		}
    +	}
    +
    +}
    
  • randomstringutils.go+2 16 modified
    @@ -20,7 +20,6 @@ import (
     	"fmt"
     	"math"
     	"math/rand"
    -	"regexp"
     	"time"
     	"unicode"
     )
    @@ -75,12 +74,10 @@ func RandomNumeric(count int) (string, error) {
     
     /*
     RandomAlphabetic creates a random string whose length is the number of characters specified.
    -Characters will be chosen from the set of alpha-numeric characters as indicated by the arguments.
    +Characters will be chosen from the set of alphabetic characters.
     
     Parameters:
     	count - the length of random string to create
    -	letters - if true, generated string may include alphabetic characters
    -	numbers - if true, generated string may include numeric characters
     
     Returns:
     	string - the random string
    @@ -106,19 +103,8 @@ func RandomAlphaNumeric(count int) (string, error) {
     	if err != nil {
     		return "", fmt.Errorf("Error: %s", err)
     	}
    -	match, err := regexp.MatchString("([0-9]+)", RandomString)
    -	if err != nil {
    -		panic(err)
    -	}
     
    -	if !match {
    -		//Get the position between 0 and the length of the string-1  to insert a random number
    -		position := rand.Intn(count)
    -		//Insert a random number between [0-9] in the position
    -		RandomString = RandomString[:position] + string('0'+rand.Intn(10)) + RandomString[position+1:]
    -		return RandomString, err
    -	}
    -	return RandomString, err
    +	return RandomString[:count], nil
     
     }
     
    
  • randomstringutils_test.go+37 0 modified
    @@ -3,6 +3,8 @@ package goutils
     import (
     	"fmt"
     	"math/rand"
    +	"regexp"
    +	"strconv"
     	"testing"
     )
     
    @@ -76,3 +78,38 @@ func ExampleRandomSeed() {
     	// H_I;E
     	// 2b2ca
     }
    +
    +func TestRandAlphaNumeric_FuzzOnlyNumeric(t *testing.T) {
    +
    +	// Testing for a reported regression in which some versions produced
    +	// a predictably small set of chars.
    +	iters := 1000
    +	charlen := 0
    +	for i := 0; i < 16; i++ {
    +		numOnly := 0
    +		charlen++
    +		for i := 0; i < iters; i++ {
    +			out, err := RandomAlphaNumeric(charlen)
    +			println(out)
    +			if err != nil {
    +				t.Fatal("func failed to produce a random thinger")
    +			}
    +			if _, err := strconv.Atoi(out); err == nil {
    +				numOnly++
    +			}
    +
    +			m, err := regexp.MatchString("^[0-9a-zA-Z]+$", out)
    +			if err != nil {
    +				t.Fatal(err)
    +			}
    +			if !m {
    +				t.Fatal("Character is not alphanum")
    +			}
    +		}
    +
    +		if numOnly == iters {
    +			t.Fatalf("Got %d numeric-only random sequences", numOnly)
    +		}
    +	}
    +
    +}
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

7

News mentions

0

No linked articles in our index yet.