【缺陷周话】第27期:不安全的随机数
字数 1944 2025-08-18 11:38:21

不安全的随机数缺陷分析与防范指南

1. 不安全的随机数概述

随机数在计算机安全领域有着广泛的应用,特别是在密码学中。然而,不正确的随机数生成方式会导致严重的安全漏洞。

1.1 常见的不安全随机数实现

在Java中,常见的错误做法包括:

  • 使用java.util.Random类生成随机数
  • 使用可预测的种子值
  • 依赖伪随机数生成器(Pseudo-Random Number Generator, PRNG)

1.2 安全关键场景

以下场景必须使用安全的随机数:

  • UUID生成
  • Token生成
  • 密钥生成
  • 密文加盐处理
  • 密码重置链接
  • 验证码生成

2. 不安全随机数的危害

2.1 直接风险

使用可预测的随机数会导致:

  • 加密密钥可被预测
  • 会话令牌可被猜测
  • 验证码可被暴力破解
  • 系统访问控制被绕过

2.2 实际漏洞案例

CVE编号 影响范围 漏洞描述
CVE-2018-1474 Android 7.0-9.0 random.c中的random_get_bytes使用不安全默认值,导致随机性降低
CVE-2018-18531 kaptcha 2.3.2 使用Random而非SecureRandom生成验证码值
CVE-2018-16031 Socket.io ≤0.9.6 使用Math.random()生成可预测的套接字ID

3. 代码示例分析

3.1 缺陷代码示例

// 不安全随机数使用示例
import java.util.Random;

public class PasswordResetLink {
    public String resetPassword(String username) {
        Random random = new Random();  // 缺陷点:使用不安全的Random类
        
        if("admin".equals(username)) {
            random.setSeed(123456L);  // 缺陷点:使用固定种子
            return scramble(random, username);
        }
        return scramble(random, username);
    }
    
    private String scramble(Random random, String username) {
        // 使用随机数处理用户名
        // ...
    }
}

缺陷分析

  1. 使用java.util.Random类生成随机数
  2. 为"admin"账户设置了固定种子(123456L)
  3. 随机数生成器可被预测

3.2 修复代码示例

// 安全随机数使用示例
import java.security.SecureRandom;

public class PasswordResetLink {
    public String resetPassword(String username) {
        SecureRandom random = new SecureRandom();  // 修复:使用安全的SecureRandom
        
        if("admin".equals(username)) {
            // 不再设置固定种子
            return scramble(random, username);
        }
        return scramble(random, username);
    }
    
    private String scramble(SecureRandom random, String username) {
        // 使用安全随机数处理用户名
        // ...
    }
}

修复要点

  1. 使用java.security.SecureRandom替代java.util.Random
  2. 不再设置固定种子
  3. SecureRandom默认使用系统提供的强随机种子

4. 安全实践指南

4.1 Java中的安全随机数

在Java中,应始终使用SecureRandom类:

import java.security.SecureRandom;

public class SecureRandomExample {
    public static void main(String[] args) {
        // 创建安全随机数生成器
        SecureRandom secureRandom = new SecureRandom();
        
        // 生成随机字节数组
        byte[] randomBytes = new byte[32];
        secureRandom.nextBytes(randomBytes);
        
        // 生成随机整数
        int randomInt = secureRandom.nextInt();
    }
}

4.2 种子管理最佳实践

  1. 不要设置固定种子:让系统自动选择强随机种子
  2. 避免种子泄露:确保种子生成过程安全
  3. 定期重新播种:对于长时间运行的应用程序

4.3 跨平台安全建议

语言/平台 安全随机数实现 不安全实现
Java SecureRandom Random, Math.random()
C/C++ CryptGenRandom (Windows), /dev/random (Unix) rand(), random()
Python os.urandom(), secrets模块 random模块
JavaScript crypto.getRandomValues() Math.random()

5. 检测与验证

5.1 静态检测方法

使用代码安全检测工具(如360代码卫士)可以检测以下模式:

  • 使用java.util.Random
  • 调用Math.random()
  • 设置固定种子
  • 使用可预测的种子源

5.2 动态验证方法

  1. 随机性测试:使用统计测试套件(如Diehard测试、NIST测试)验证随机数质量
  2. 熵源检查:确保系统有足够的熵源
  3. 预测测试:尝试基于部分输出预测后续随机数

6. 高级主题

6.1 密码学安全伪随机数生成器(CSPRNG)

SecureRandom默认使用SHA1PRNG算法,这是符合CSPRNG要求的实现。其他安全算法包括:

  • HMAC_DRBG
  • Hash_DRBG
  • CTR_DRBG

6.2 系统熵源

安全随机数生成依赖于系统熵源:

  • Linux: /dev/random (阻塞型), /dev/urandom (非阻塞型)
  • Windows: CryptGenRandom API
  • 硬件随机数生成器(HRNG)

6.3 性能考量

虽然SecureRandom比Random慢,但在安全关键场景中不应妥协。对于高吞吐量场景:

  • 可以预生成一批随机数
  • 使用线程安全的实现
  • 考虑硬件加速方案

7. 总结

在安全开发中,随机数的正确使用至关重要。遵循以下原则:

  1. 在安全关键场景始终使用SecureRandom等安全实现
  2. 避免使用固定或可预测的种子
  3. 定期进行代码审计和安全测试
  4. 了解不同平台的推荐实现方式
  5. 在性能与安全之间做出明智权衡

通过遵循这些准则,可以有效防止因随机数不安全导致的各类安全漏洞。

不安全的随机数缺陷分析与防范指南 1. 不安全的随机数概述 随机数在计算机安全领域有着广泛的应用,特别是在密码学中。然而,不正确的随机数生成方式会导致严重的安全漏洞。 1.1 常见的不安全随机数实现 在Java中,常见的错误做法包括: 使用 java.util.Random 类生成随机数 使用可预测的种子值 依赖伪随机数生成器(Pseudo-Random Number Generator, PRNG) 1.2 安全关键场景 以下场景必须使用安全的随机数: UUID生成 Token生成 密钥生成 密文加盐处理 密码重置链接 验证码生成 2. 不安全随机数的危害 2.1 直接风险 使用可预测的随机数会导致: 加密密钥可被预测 会话令牌可被猜测 验证码可被暴力破解 系统访问控制被绕过 2.2 实际漏洞案例 | CVE编号 | 影响范围 | 漏洞描述 | |---------|----------|----------| | CVE-2018-1474 | Android 7.0-9.0 | random.c中的random_ get_ bytes使用不安全默认值,导致随机性降低 | | CVE-2018-18531 | kaptcha 2.3.2 | 使用Random而非SecureRandom生成验证码值 | | CVE-2018-16031 | Socket.io ≤0.9.6 | 使用Math.random()生成可预测的套接字ID | 3. 代码示例分析 3.1 缺陷代码示例 缺陷分析 : 使用 java.util.Random 类生成随机数 为"admin"账户设置了固定种子(123456L) 随机数生成器可被预测 3.2 修复代码示例 修复要点 : 使用 java.security.SecureRandom 替代 java.util.Random 不再设置固定种子 SecureRandom默认使用系统提供的强随机种子 4. 安全实践指南 4.1 Java中的安全随机数 在Java中,应始终使用 SecureRandom 类: 4.2 种子管理最佳实践 不要设置固定种子 :让系统自动选择强随机种子 避免种子泄露 :确保种子生成过程安全 定期重新播种 :对于长时间运行的应用程序 4.3 跨平台安全建议 | 语言/平台 | 安全随机数实现 | 不安全实现 | |-----------|----------------|------------| | Java | SecureRandom | Random, Math.random() | | C/C++ | CryptGenRandom (Windows), /dev/random (Unix) | rand(), random() | | Python | os.urandom(), secrets模块 | random模块 | | JavaScript | crypto.getRandomValues() | Math.random() | 5. 检测与验证 5.1 静态检测方法 使用代码安全检测工具(如360代码卫士)可以检测以下模式: 使用 java.util.Random 类 调用 Math.random() 设置固定种子 使用可预测的种子源 5.2 动态验证方法 随机性测试 :使用统计测试套件(如Diehard测试、NIST测试)验证随机数质量 熵源检查 :确保系统有足够的熵源 预测测试 :尝试基于部分输出预测后续随机数 6. 高级主题 6.1 密码学安全伪随机数生成器(CSPRNG) SecureRandom默认使用SHA1PRNG算法,这是符合CSPRNG要求的实现。其他安全算法包括: HMAC_ DRBG Hash_ DRBG CTR_ DRBG 6.2 系统熵源 安全随机数生成依赖于系统熵源: Linux: /dev/random (阻塞型), /dev/urandom (非阻塞型) Windows: CryptGenRandom API 硬件随机数生成器(HRNG) 6.3 性能考量 虽然SecureRandom比Random慢,但在安全关键场景中不应妥协。对于高吞吐量场景: 可以预生成一批随机数 使用线程安全的实现 考虑硬件加速方案 7. 总结 在安全开发中,随机数的正确使用至关重要。遵循以下原则: 在安全关键场景始终使用SecureRandom等安全实现 避免使用固定或可预测的种子 定期进行代码审计和安全测试 了解不同平台的推荐实现方式 在性能与安全之间做出明智权衡 通过遵循这些准则,可以有效防止因随机数不安全导致的各类安全漏洞。