Skip to Content
Course content

154: Generating Secure Random Values with crypto/rand

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Imagine you're running a casino. For the slot machines, you might use a high-quality mechanical gear system that produces "random" results. It feels random to the player, but if you know the exact starting position of the gears and how many times the lever was pulled, you could theoretically predict every single outcome. That's basically how math/rand works in Go—it's a mathematical formula that mimics randomness based on a starting "seed."

Now, imagine you're designing the lock for the casino's main vault. You can't use the slot machine gears; a clever thief could figure out the pattern. Instead, you use a system that measures the microscopic, unpredictable vibrations of the building's foundation or the atmospheric noise in the room. There is no "starting seed" to steal because the source is the chaotic reality of the physical world. That is what crypto/rand does. It taps into the operating system's entropy pool—stuff like hardware interrupts and mouse movements—to give you numbers that are cryptographically secure.

The danger of predictable patterns

I've seen developers use math/rand for password reset tokens because it's faster and the API is simpler. That is a massive security hole. Since math/rand is deterministic, if an attacker can guess your seed (which is often just the current Unix timestamp), they can generate every token your server will ever produce. When security is on the line—tokens, salts, keys, or session IDs—you must use crypto/rand. Period.

Filling buffers with system entropy

Unlike math/rand, which gives you a neat Intn() function to get a number between 1 and 10, crypto/rand works differently. It doesn't return a single number; it fills a slice of bytes. You provide the "bucket" (the byte slice), and the package fills it with random noise from the OS.

package main

import (
	"crypto/rand"
	"encoding/hex"
	"fmt"
	"log"
)

func main() {
	// We create a slice of 32 bytes. 
	// This gives us 256 bits of entropy, which is the industry standard for high security.
	b := make([]byte, 32)

	// rand.Read fills the slice. It returns the number of bytes read and an error.
	_, err := rand.Read(b)
	if err != nil {
		log.Fatal("Something went wrong with the system entropy:", err)
	}

	// Since raw bytes are hard to store or send in a URL, 
	// we encode them into a hex string.
	token := hex.EncodeToString(b)
	fmt.Printf("Your secure token: %s\n", token)
}

Mapping the randomness to a specific range

Sometimes you don't want a giant hex string; maybe you just need a secure random integer for a lottery draw or a verification code. In these cases, you shouldn't try to "math" your way into a range using the modulo operator (%), as that can introduce "modulo bias," making some numbers slightly more likely than others.

Instead, use rand.Int from the crypto/rand package. It takes a big.Int as a limit and handles the complexity of ensuring the distribution is perfectly uniform. It's a bit more verbose because it deals with arbitrary-precision integers, but it's the only way to stay truly secure.

package main

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

func main() {
	// We want a number between 0 and 999,999
	max := big.NewInt(1000000)

	// rand.Int returns a random value in [0, max)
	n, err := rand.Int(rand.Reader, max)
	if err != nil {
		panic(err)
	}

	fmt.Printf("Your secure 6-digit code: %06d\n", n)
}



📋 Practical Task

Build a Secure API Key Generator

Your task is to create a professional-grade API key generator. In a real production environment, API keys need to be long enough to be unguessable and encoded in a way that is safe for HTTP headers.

Write a Go program that does the following:

  • Defines a function GenerateAPIKey(length int) (string, error).
  • Inside the function, use crypto/rand to generate a random byte slice of the specified length.
  • Convert those random bytes into a Base64 encoded string (using the encoding/base64 package) instead of hex.
  • In your main function, call this generator to create a 48-byte key and print it to the console.
  • Ensure you properly handle the error returned by rand.Read.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.