SecureRandom是生成高安全性变量的强制要求,必须使用CSPRNG确保不可预测性,禁用Random/Math.random()、固定seed、低熵源等不安全做法,推荐单例复用并规范编码输出。

在高安全性变量场景下,SecureRandom不是“可选”,而是强制要求。这类变量包括会话令牌(session token)、API密钥、密码重置码、加密密钥、一次性验证码(TOTP 种子)、CSRF token 等——任何一旦被预测或重复就可能导致账户接管、数据泄露或权限越权的值。
必须满足不可预测性
攻击者即使获取了此前生成的多个随机值,也无法推算出下一个值。SecureRandom 通过密码学安全的伪随机数生成器(CSPRNG)实现这一点,而普通 Random 或 Math.random() 使用线性同余算法,输出具有明显统计规律和可复现性。
- 不要用 new Random().nextInt(1000000) 生成短信验证码
- 避免用 System.currentTimeMillis() + Math.random() 拼接生成 token
- 禁止对 SecureRandom 实例反复 setSeed(固定值),这会退化为确定性序列
必须依赖高质量熵源
SecureRandom 初始化时需从操作系统采集足够熵(如 Linux 的 /dev/urandom),确保初始种子不可猜测。若环境熵不足(如容器启动初期、低配虚拟机),默认 NativePRNG 可能延迟甚至阻塞。
- 生产环境建议添加 JVM 参数:-Djava.security.egd=file:/dev/./urandom(绕过 /dev/random 阻塞)
- 避免在单元测试中无意识触发熵采集;可用 SecureRandom.getInstanceStrong() 或预设 seed 做可控模拟
- 不推荐显式调用 generateSeed(n) 后再 setSeed(),除非你清楚该 seed 来自可信熵源
必须保证输出无偏移且定长
安全变量常需固定长度、无格式偏差的原始字节。例如生成 256 位 AES 密钥,必须恰好 32 字节,且每个字节取值均匀分布于 [0, 255]。
- ✅ 正确:nextBytes(new byte[32])
- ❌ 错误:nextInt(1000000)(范围受限、低位偏差)、toString() + hashCode()(完全不可控)
- 编码输出时用标准 Base64 或 hex:Base64.getEncoder().encodeToString(bytes),而非手写转换逻辑
必须注意并发与复用策略
SecureRandom 实例是线程安全的,但部分算法(如旧版 SHA1PRNG)在高并发下内部状态更新慢,可能成为瓶颈;而频繁新建实例又带来熵初始化开销。
- 推荐单例复用(如 Spring 中声明为 @Bean),避免在循环或高频接口中 new SecureRandom()
- Web 应用中不要将同一实例长期绑定到用户 session 或 request 作用域
- 如需隔离上下文(如多租户密钥派生),可基于主 SecureRandom 衍生子实例:secureRandom.generateSeed(16) 作为子种子

















