Go
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Functions and Methods
-
Section 4: Concurrency
-
Section 5: Packages and Tooling
-
Section 6: More Standard Library
-
Section 7: Building Services
-
Section 8: Advanced Go
-
Section 9: Go in the Cloud-Native Ecosystem
-
Section 10: Data Structures and Algorithms in Go
-
Section 11: Testing and Deployment
-
Section 12: Practical Projects
-
Section 13: More Standard Library Practice
-
Section 14: More Practice Projects
-
Section 15: Design Patterns in Go
-
Section 16: Interview Practice
-
Section 17: Package fmt In Depth
-
Section 18: Package strings and strconv
-
Section 19: Package os and io
-
Section 20: Package time
-
Section 21: Package sort and container
-
Section 22: Package encoding
-
Section 23: Package net/http In Depth
-
Section 24: Package context
-
Section 25: Package regexp and bytes
-
Section 26: Package errors In Depth
-
Section 27: Package crypto and hash
-
Section 28: Package flag and log
-
Section 29: Package sync In Depth
-
Section 30: More Practice Exercises
-
Section 31: Go Modules and Workspaces In Depth
-
Section 32: Generics Deep Dive (Go 1.18+)
-
Section 33: Testing Package In Depth
-
Section 34: More Interview and Whiteboard Practice
-
Section 35: Package math and unicode
-
Section 36: Package path and filepath
-
Section 37: Package database/sql
-
Section 38: Package text/template and html/template
-
Section 39: Package archive and compress
-
Section 40: Lower-Level net Package
-
Section 41: Package runtime and reflect
-
Section 42: Package embed and io/fs
-
Section 43: Go Assembly and CGO Basics
-
Section 44: Building CLIs and TUIs
-
Section 45: Go Performance Tuning
-
Section 46: More Real-World Projects
-
Section 47: Go in Production
-
Section 48: Go Design Patterns
-
Section 49: Go Interfaces Deep Dive
-
Section 50: Final Practice and Review
154: Generating Secure Random Values with crypto/rand
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/randto generate a random byte slice of the specified length. - Convert those random bytes into a Base64 encoded string (using the
encoding/base64package) instead of hex. - In your
mainfunction, 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.
There are no comments for now.