Secure generation of random numbers
package main
import (
"fmt"
"math/rand"
"time"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.GET("/random", func(c *gin.Context) {
rand.Seed(time.Now().UnixNano())
result := rand.Intn(100)
c.JSON(200, gin.H{
"randomNumber": result,
})
})
r.Run()
}
The above code is a simple Gin framework based Go application that generates a random number when the
/random
endpoint is hit.
The vulnerability lies in the usage of the
math/rand
package to generate random numbers. The
rand.Seed(time.Now().UnixNano())
line is used to seed the random number generator. The seed for the random number generator is the current time in nanoseconds since 1970. This is a common but insecure way of seeding a random number generator as it is predictable, especially if the attacker knows the approximate time the numbers were generated.
Furthermore, the
rand.Intn(100)
function generates a pseudo-random number in the range [0,100). The range is small and the function is deterministic, meaning it will produce the same sequence of numbers for the same seed. If an attacker can guess or know the seed, they can predict the sequence of random numbers.
This vulnerability could potentially be exploited in various ways, depending on the context in which the random numbers are used. For example, if these numbers are used for session IDs, OTPs, or cryptographic keys, it could lead to session hijacking, OTP bypass, or decryption of sensitive data.
package main
import (
"crypto/rand"
"encoding/binary"
"fmt"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.GET("/random", func(c *gin.Context) {
var n int64
binary.Read(rand.Reader, binary.BigEndian, &n)
result := n % 100
c.JSON(200, gin.H{
"randomNumber": result,
})
})
r.Run()
}
The original code was using the
math/rand
package to generate random numbers. This package is not secure for generating random numbers as it uses a pseudo-random number generator which can be predictable and hence, exploitable.
The updated code uses the
crypto/rand
package which is a cryptographically secure random number generator. This package reads from
/dev/urandom
on Unix-like systems and
CryptGenRandom
on Windows, both of which are considered to be secure sources of randomness.
The
binary.Read(rand.Reader, binary.BigEndian, &n)
line reads a random int64 from the
rand.Reader
which is a global, shared instance of a cryptographically secure random number generator.
The
result := n % 100
line ensures that the generated random number is within the range of 0 to 99.
This change makes the random number generation in the application secure and unpredictable, thus fixing the vulnerability.