crypto/rand.read是唯一可靠入口,因其直接对接操作系统熵源(如/dev/urandom)、不依赖种子、不可重现;必须传已分配切片、检查error、禁用模运算,整数需用rand.int配合拒绝采样。

必须用 crypto/rand,不能用 math/rand——后者生成的随机数可被预测,用于 token、密钥、salt 就等于把门钥匙贴在门上。
为什么 crypto/rand.Read 是唯一可靠入口
crypto/rand.Read 直接对接操作系统熵源(Linux 用 /dev/urandom,Windows 用 BCryptGenRandom),不依赖种子、不缓存、不可重现。它只做一件事:把真随机字节塞进你给的切片里。
- 必须传入已分配长度的切片,例如
buf := make([]byte, 32);传nil或未初始化切片会 panic - 返回值
n理论上应等于len(buf),但极少数情况(如容器无熵、嵌入式设备)可能返回io.ErrUnexpectedEOF,err 必须检查,不能忽略 - 别对同一
[]byte多次调用Read——单次已满足密码学强度,重复调用既无增益,还可能掩盖错误 - 不要包装
rand.Reader成私有实例,它本身是线程安全的全局变量,封装反而误导使用者以为可替换
生成 [0, n) 安全整数只能用 rand.Int
crypto/rand 不提供 Intn 这类便捷函数,因为模运算(% n)必然引入偏差:256 字节空间无法被非 2 的幂整除,小数字概率略高。正确路径只有一条:rand.Int(rand.Reader, big.NewInt(n))。
- 它内部已实现拒绝采样(rejection sampling),保证均匀分布
- 返回
*big.Int是刻意设计——避免溢出、支持任意精度(比如生成 RSA 密钥时需上千位) - 若
n ≤ math.MaxInt64,可安全转成原生类型:nBig.Int64() - 别手写循环 + 模运算拼字符,源头就不安全;也别用
binary.Read或unsafe强转字节,易出偏移
生成 URL-safe 随机字符串的惯用写法
先填字节,再编码,不拼字母表。直接用 base64.URLEncoding 或更优的 base64.RawURLEncoding(无 +、/、=,适合 HTTP header 和文件名)。
- 要 16 字符 token?就
make([]byte, 16),一次读满,别循环调用小 buffer - 别用
fmt.Sprintf("%x", buf)做 hex 编码——太长且无意义;也别用strings.Map后期替换字符,那是在污染数据流 - 如果必须用纯字母数字(如短链接码),得自己做拒绝采样映射:每次读 1 字节,只接受
0–61,超出丢弃重读;平均 1.1 次就能拿到一个有效值 - 避免
base64.StdEncoding:含+和/,某些代理或 URL 解析器会误处理
最常被跳过的其实是错误处理和 buffer 分配逻辑——不是“几乎不会出错”就可以不检查 err,也不是“反正快”就反复小块读;这些细节在容器或 distroless 镜像里会突然暴露为 panic 或空 token。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











