CVE-2024-21495 (Go, published 2024-02-17T05:15Z)
Description: value: Versions of the package github.com/greenpau/caddy-security before 1.0.42 are vulnerable to Insecure Randomness due to using an insecure random number generation library which could possibly be predicted via a brute-force search. Attackers could use the potentially predictable nonce value used for authentication purposes in the OAuth flow to conduct OAuth replay attacks. In addition, insecure randomness is used while generating multifactor authentication (MFA) secrets and creating API keys in the database package.
Severity: nan (CVSS v3 base score: )
Weakness: NVD-CWE-noinfo \u2014 Insufficient Information
There is insufficient information about the issue to classify it; details are unkown or unspecified.
Repository: https://github.com/greenpau/go-authcrunch
Files changed:
Makefile
internal/tests/random.go
pkg/identity/api_key.go
pkg/identity/api_key_test.go
pkg/identity/database.go
pkg/identity/mfa_token.go
pkg/identity/public_key.go
pkg/util/random.go
Commit message: security: addresses insecure randomness finding

See also: https://github.com/greenpau/caddy-security/issues/265


Vulnerable code:
	@time richgo test $(VERBOSE) $(TEST) -coverprofile=.coverage/coverage.out -run NewAPIKey ./pkg/identity/...
	"math/rand"
	var b strings.Builder
	for i := 0; i < length; i++ {
		b.WriteRune(chars[rand.Intn(len(chars))])
	return b.String()
		ID:        GetRandomString(40),
					Payload: GetRandomStringFromRange(54, 72),
					Payload:  GetRandomStringFromRange(54, 72),
					Payload:  GetRandomStringFromRange(54, 72),
					Payload:  GetRandomStringFromRange(54, 72),
					Payload:  GetRandomStringFromRange(54, 72),
	s := GetRandomStringFromRange(72, 96)
		ID:         GetRandomString(40),
		ID:        GetRandomString(40),
// Copyright 2022 Paul Greenberg greenpau@outlook.com
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package identity

import (
	"math/rand"
	"time"
)

const charset = "abcdefghijklmnopqrstuvwxyz" +
	"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"

var seed *rand.Rand = rand.New(
	rand.NewSource(time.Now().UnixNano()),
)

func gen(length int, charset string) string {
	b := make([]byte, length)
	for i := range b {
		b[i] = charset[seed.Intn(len(charset))]
	}
	return string(b)
}

// GetRandomString returns X character long random string.
func GetRandomString(i int) string {
	if i < 1 {
		i = 40
	}
	return gen(i, charset)
}

// GetRandomStringFromRange generates random string of a random length. The
// random lenght is bounded by a and b.
func GetRandomStringFromRange(a, b int) string {
	var i int
	if a > b {
		i = rand.Intn(a-b) + b
	} else {
		i = rand.Intn(b-a) + a
	}
	return gen(i, charset)
}
	"math/rand"
	"time"
var seed *rand.Rand = rand.New(
	rand.NewSource(time.Now().UnixNano()),
)
func gen(length int, charset string) string {
	for i := range b {
		b[i] = charset[seed.Intn(len(charset))]
	return gen(i, charset)
	var i int
		i = rand.Intn(a-b) + b
		i = rand.Intn(b-a) + a
	var i int
		i = rand.Intn(a-b) + b
		i = rand.Intn(b-a) + a

Fixed code:
	@#time richgo test -v -coverprofile=.coverage/coverage.out internal/testutils/*.go
	@time richgo test $(VERBOSE) $(TEST) -coverprofile=.coverage/coverage.out ./pkg/util/...
	@#time richgo test $(VERBOSE) $(TEST) -coverprofile=.coverage/coverage.out -run NewAPIKey ./pkg/identity/...
	"crypto/rand"
	"io"
	mathrand "math/rand"
	charsLen := byte(36)


	b := make([]byte, length)
	if _, err := io.ReadFull(rand.Reader, b); err != nil {
		var sb strings.Builder
		for i := 0; i < length; i++ {
			sb.WriteRune(chars[mathrand.Intn(len(chars))])
		}
		return sb.String()

	for i, char := range b {
		b[i] = byte(chars[char%charsLen])
	}

	return string(b)
	"github.com/greenpau/go-authcrunch/pkg/util"
		ID:        util.GetRandomString(40),
	"github.com/greenpau/go-authcrunch/pkg/util"
					Payload: util.GetRandomStringFromRange(54, 72),
					Payload:  util.GetRandomStringFromRange(54, 72),
					Payload:  util.GetRandomStringFromRange(54, 72),
					Payload:  util.GetRandomStringFromRange(54, 72),
					Payload:  util.GetRandomStringFromRange(54, 72),
	"github.com/greenpau/go-authcrunch/pkg/util"
	s := util.GetRandomStringFromRange(72, 96)
	"github.com/greenpau/go-authcrunch/pkg/util"
		ID:         util.GetRandomString(40),
	"github.com/greenpau/go-authcrunch/pkg/util"
		ID:        util.GetRandomString(40),
	"crypto/rand"
	"io"
	//	"math"
	"math/big"
	mathrand "math/rand"
func genRandInt(i int) uint32 {
	n, err := rand.Int(rand.Reader, big.NewInt(int64(i)))
	if err != nil {
		return uint32(mathrand.Intn(i))
	}
	//if n.Uint64() > math.MaxUint32+1 {
	//	return uint32(n.Uint64() & uint32(0xFFFFFFFF))
	//}
	return uint32(n.Uint64())
}
func gen(length uint32, charset string) string {
	charsetLen := byte(len(charset))
	if _, err := io.ReadFull(rand.Reader, b); err != nil {
		// for i uint32 := 0; i < length; i++ {
		for i := uint32(0); i < length; {
			b[i] = charset[mathrand.Intn(len(charset))]
		}
		return string(b)

	for i, char := range b {
		b[i] = byte(charset[char%charsetLen])
	}

	return gen(uint32(i), charset)
	var i uint32
		i = genRandInt(a-b) + uint32(b)
		i = genRandInt(b-a) + uint32(a)
	var i uint32
		i = genRandInt(a-b) + uint32(b)
		i = genRandInt(b-a) + uint32(a)