区块链应用中常见的密码学风险

September 30, 2025 · View on GitHub

1、私钥随机数安全

使用 JavaScript Math.random 或基于时间的种子生成随机数

  • 严重性

  • 描述

    JavaScript 的 Math.random() 是一个伪随机数生成器(PRNG),不适合用于加密安全的场景。其实现依赖于浏览器或 JavaScript 引擎(如 V8 的 Xorshift128+),种子和算法通常不可控且不可预测性不足,可能导致生成的随机数序列被攻击者猜测或重现。

  • 利用场景

    这在生成加密密钥、会话令牌、CSRF 令牌或游戏随机事件时,可能导致密钥被破解、会话劫持或游戏作弊。

  • 建议

    优先使用 crypto.getRandomValues()(Web Crypto API)生成加密安全的随机数,适用于密钥、令牌等敏感场景。

使用 Java 的不安全随机数生成方式生成私钥

  • 严重性

  • 描述

    Java 的 java.util.Random 或 java.util.concurrent.ThreadLocalRandom 是非加密安全的伪随机数生成器(PRNG),其种子和算法(如线性同余生成器)可预测且熵不足。如果使用这些方法生成加密私钥(如 RSA、ECDSA),生成的密钥可能存在可预测性,容易被攻击者推导或重现。

  • 利用场景

    在基于 Java 的 Web 应用中,使用 Random 生成的密钥可能被攻击者利用,破解 TLS 会话或伪造 JWT 令牌。

  • 建议

    改用 java.security.SecureRandom,它是为加密场景设计的随机数生成器,提供高熵输出,适合生成私钥。或者优先使用标准库或框架(如 KeyPairGenerator、KeyFactory)生成密钥。

Android 系统在某些版本中 SecureRandom 未能正确初始化

  • 严重性

  • 描述

    在某些 Android 版本(特别是早期版本,如 4.1-4.3),java.security.SecureRandom 未能正确初始化,导致其生成的随机数熵不足。这通常是由于系统熵池(如 /dev/urandom)未被充分填充,或者 SecureRandom 的实现缺陷,致使生成的随机数序列可预测性较高,用于私钥生成时显著降低安全性。

  • 利用场景

    在受影响的 Android 设备上,使用 SecureRandom 生成的 Bitcoin 私钥可能被破解,导致资金被盗。

  • 建议

    在调用 SecureRandom 前,显式调用 SecureRandom.setSeed() 并结合高熵来源(如用户输入或硬件传感器数据)增强随机性。

私钥生成过程中存储随机数的变量类型空间太小

  • 严重性

  • 描述

    在私钥生成过程中,若使用空间太小的变量类型(如 32 位整数)存储随机数,会限制随机数的范围和熵值,导致生成的私钥强度不足。

  • 利用场景

    攻击者可利用随机数范围受限的弱点,通过暴力破解或预计算攻击(如彩虹表),快速猜测私钥。例如,在 Profanity 工具的案例中,攻击者通过分析生成的地址模式,成功破解多个 Ethereum 钱包,窃取大量资金。

    cl_ulong4 Dispatcher::Device::createSeed() {
    #ifdef PROFANITY_DEBUG
        cl_ulong4 r;
        r.s[0] = 1;
        r.s[1] = 1;
        r.s[2] = 1;
        r.s[3] = 1;
        return r;
    #else
        // Randomize private keys
        std::random_device rd;
        std::mt19937_64 eng(rd()); //@audit rd() The returned value is a 32-bit length random value.
        std::uniform_int_distribution<cl_ulong> distr;
        cl_ulong4 r;
        r.s[0] = distr(eng);
        r.s[1] = distr(eng);
        r.s[2] = distr(eng);
        r.s[3] = distr(eng);
        return r;
    #endif
    }
    
  • 建议

    使用足够大的变量类型(如 256 位或更高)存储随机数,以支持完整密钥空间(如 secp256k1 曲线的 22562^{256} 范围)。

Libbitcoin Mersenne Twister 弱熵漏洞

  • 严重性

  • 描述

    Libbitcoin Explorer(版本 3.0.0 至 3.6.0)的 bx seed 命令使用 Mersenne Twister (MT19937) 伪随机数生成器(PRNG)生成钱包种子,种子仅由 32 位系统时间(high_resolution_clock)初始化。这导致熵空间被限制在 2322^{32}(约 43 亿)个可能值,远低于安全的 128 位或 256 位熵要求。攻击者可通过暴力破解重现种子,进而推导私钥,危及用户资金安全。此漏洞被称为“Milk Sad”,因其生成的首个种子短语以“milk sad”开头。

  • 利用场景

    get_clock_seed() 返回仅 32 位的系统时间戳(uint32_t),用作 Mersenne Twister 的种子。std::mt19937 虽生成看似随机的输出,但其熵受限于 32 位种子,无法提供 128 位或更高安全级别。pseudo_random_fill 填充输出时,扩展到 256 位仅是伪随机扩展,未增加实际熵。

    void pseudo_random_fill(data_chunk& out)
    {
        return pseudo_random::fill<data_chunk>(out);
    }
    
    static uint32_t get_clock_seed()
    {
        const auto now = high_resolution_clock::now();
        return static_cast<uint32_t>(now.time_since_epoch().count());
    }
    
    std::mt19937& pseudo_random::get_twister()
    {
        // Boost.thread will clean up the thread statics using this function.
        const auto deleter = [](std::mt19937* twister)
        {
            delete twister;
        };
    
        // Maintain thread static state space.
        static boost::thread_specific_ptr<std::mt19937> twister(deleter);
    
        // This is thread safe because the instance is thread static.
        if (twister.get() == nullptr)
        {
            // Seed with high resolution clock.
            twister.reset(new std::mt19937(get_clock_seed()));
        } //@audit Used system time, and only 32-bit space instead of 256-bit.
    
        return *twister;
    }
    
  • 建议

    替换 Mersenne Twister,改用加密安全的随机数生成器(如 /dev/urandom、C++ 的 std::random_device 搭配高熵源,或 OpenSSL 的 RAND_bytes)。

OpenSSL 随机数生成器安全风险

  • 严重性

  • 描述

    OpenSSL 密码库的随机数生成器RAND_pseudo_bytes() 用于将 num 伪随机字节放入 buf,但存在安全设计缺陷。由 RAND_pseudo_bytes() 生成的伪随机字节序列如果长度足够长,则是唯一的,但并不一定是不可预测的。它们可以用于非加密目的,并且可以在某些加密协议中用于特定目的,但通常不用于密钥生成等。

  • 漏洞示例:

    低熵密钥泄露(LESLI):

    // 应用程序临时存储用户PIN码
    char pin[4] = "1234";
    // 安全地清除PIN码(看似合理的做法)
    RAND_pseudo_bytes((unsigned char*)pin, sizeof(pin));
    // 生成一些随机数作为nonce
    unsigned char nonce[16];
    RAND_pseudo_bytes(nonce, sizeof(nonce));
    // 问题:nonce中可能泄露了擦除的PIN码信息
    

    攻击者通过获取nonce并通过暴力枚举所有可能的PIN码和RNG状态,可以恢复原始的PIN值。

  • 建议:

    避免使用**RAND_pseudo_bytes,**完全替换为RAND_bytes并检查其返回值。

2、ECDSA 安全

secp256r1 后门问题

  • 严重性

  • 描述

    secp256r1(也称 NIST P-256)是一种广泛使用的椭圆曲线密码算法,但存在对其生成参数潜在后门的担忧。标准化过程中,参数由 NSA 提供,缺乏透明的生成过程,可能被故意设计为包含弱点,允许特定攻击者(如 NSA)利用隐藏的数学关系解密数据或伪造签名。

  • 利用场景

    攻击者可能利用后门(若存在)通过已知参数的数学特性,快速计算私钥或预测随机数生成器的输出,从而破解加密通信、伪造数字签名,或窃取基于 secp256r1 的加密系统(如 TLS、Bitcoin、SSH)中的敏感数据。

  • 建议

    考虑切换到 Ed25519 或 Secp256k1 等透明生成参数的曲线,降低后门风险。

secp256k1 中 k 值弱随机导致私钥泄露

  • 严重性

  • 描述

    在 secp256k1 曲线的 ECDSA 签名过程中,签名需要一个随机数 k(nonce)。如果 k 值由弱随机数生成器生成(例如,低熵源、不安全的 PRNG 或可预测的种子),攻击者可能通过分析签名数据推测 k 值,进而利用 ECDSA 的数学特性直接计算私钥。这种漏洞通常源于使用不安全的随机数生成器(如 rand()、Math.random())或环境熵不足(如虚拟机或嵌入式设备)。

  • 利用场景

    攻击者可通过收集少量签名数据(例如,r 和 s 值),结合弱随机数的可预测性,重构 k 值并推导私钥。在区块链场景(如 Bitcoin、Ethereum),这可能导致钱包私钥暴露,资金被盗。例如,若 k 值基于时间戳或固定种子生成,攻击者可通过暴力破解或模式分析快速恢复私钥。类似问题曾出现在早期加密钱包实现中,因使用弱随机源生成 k 值而被破解。

  • 建议

    使用确定性 k 值生成(RFC 6979),基于私钥和消息哈希生成唯一的 k 值,避免依赖随机数生成器的质量。

secp256k1 中 k 值重用导致私钥泄露

  • 严重性

  • 描述

    在 secp256k1 椭圆曲线(广泛用于比特币等加密货币)上使用 ECDSA 签名时,如果两次签名使用了相同的随机数 k(nonce),则签名中的 r 值将相同。攻击者可通过分析这两组签名 (r, s1) 和 (r, s2) 及对应的消息哈希 h1、h2,推导出私钥。这种漏洞源于 ECDSA 签名方程的数学结构,k 的重用使得攻击者能够建立方程组,直接解出私钥 d。

  • 漏洞示例

    假设用户对两条消息 m1 和 m2 使用同一私钥 d 和相同的 k 签名,生成签名 (r, s1) 和 (r, s2),其中:

    s1 = k⁻¹(h1 + dr) mod n(h1 为 m1 的哈希)
    s2 = k⁻¹(h2 + dr) mod n(h2 为 m2 的哈希)
    r 值相同,因为 r 由 k 和曲线点计算得出,与消息无关。攻击者获取这两组签名后,计算:
    1. s1 - s2 = k⁻¹(h1 + dr - h2 - dr) = k⁻¹(h1 - h2) mod n
    2. k = (h1 - h2) / (s1 - s2) mod n
    3. 再从 s1 = k⁻¹(h1 + dr) mod n 解出 d = (s1k - h1) / r mod n
    
  • 建议

    遵循 RFC 6979 标准,根据私钥和消息哈希生成确定性但不可预测的 k 值,防止重用。

ECDSA 签名值的可锻造性

  • 严重性

  • 描述

    ECDSA(椭圆曲线数字签名算法)的签名值 (r, s) 存在可锻造性,即给定一个有效签名 (r, s),可以生成另一个等价的有效签名 (r, -s mod n),其中 n 是椭圆曲线的阶。这种数学特性源于 ECDSA 签名验证的模运算对称性。如果实现未规范化签名(例如,强制使用“低 s 值”),攻击者可修改签名而不影响其有效性,可能绕过签名验证机制或破坏某些协议的唯一性要求。

  • 利用场景

    攻击者可利用签名可锻造性在区块链(如 Bitcoin、Ethereum)或协议中制造问题。例如,在 Bitcoin 交易中,攻击者可修改交易签名的 s 值生成新签名,改变交易 ID(txid),导致交易被拒绝或引发双花风险。此外,在某些智能合约或多重签名协议中,未规范化签名可能被用来绕过验证逻辑,造成资金损失或协议失效。2013 年,Bitcoin 网络曾因签名可锻造性引发交易可塑性(malleability)攻击,影响 Mt. Gox 等交易所。

  • 建议

    强制使用“低 s 值”(s ≤ n/2),遵循 RFC 6979 或 BIP-66 标准(Bitcoin 社区采用),拒绝非规范签名以消除可锻造性。

ECDSA 与 Schnorr 签名共用随机数 k 导致私钥泄露

  • 严重性

  • 描述

    在椭圆曲线数字签名算法(ECDSA)和 Schnorr 签名中,如果在生成签名时共用相同的随机数 k(nonce),攻击者可以通过分析签名对推导出私钥。这种漏洞源于两种签名方案的数学结构相似性,允许通过已知的签名方程反推出私钥。无论是在同一系统内多次签名,还是在不同签名算法间重用 k,都会导致私钥完全暴露,进而使攻击者能够伪造签名或控制相关账户。

  • 漏洞示例

    假设用户使用同一私钥 d 和随机数 k 生成 ECDSA 签名 (r, s) 和 Schnorr 签名 (r, s')。

    ECDSA 签名满足:s = k⁻¹(h + dr) mod n,其中 h 是消息哈希,r 是椭圆曲线点的 x 坐标,n 是曲线阶。

    Schnorr 签名满足:s' = k + dh mod n,其中 h 是消息哈希。

    攻击者获取两组签名后,可以通过联立方程消去 k,计算私钥 d:从 Schnorr 签名得 k = s' - dh mod n,代入 ECDSA 签名方程可以推导出:

    d=(ss’-h) / (sh+r) mod n

    实际案例中,某些加密货币钱包或智能合约可能因错误实现(如重复使用 k 或确定性 k 生成不当)导致此类漏洞。

  • 建议

    RFC6979 中输⼊参数可以包含 “addition data”,在派⽣ k 时,可以将签名算法的信息填⼊该字段,如此可以在算法的维度上安全重⽤ k。

ECDSA 不需要提供签名值对应的消息 m 时可伪造签名值

  • 严重性

  • 描述

    在验证 ECDSA 签名时,若验证过程仅要求提供消息的哈希值而非原始消息本身,则攻击者可以在不知道私钥的情况下,基于已知的合法签名,构造出能通过验证的伪造签名。这个漏洞利用了 ECDSA 验证机制的数学特性,允许攻击者选择特定的数值组合创建伪造签名,而无需知道对应的原始消息或私钥。

  • 漏洞示例

    假设存在一个合法签名 σ=(r,s) 对应消息 m 和哈希值 e=H(m),攻击者可以:

    1. 选择随机数 u,v∈F*n
    2. 计算 R'=(x', y')=uG+vP,其中 G 为基点,P 为公钥
    3. 令 r'≡x' mod n
    4. 计算 s'≡r'v^-1 mod n
    5. 计算 e'≡r'uv^-1 mod n

    此时 (r',s') 与哈希值 e' 构成的签名能够通过验证,但攻击者无法找到满足 H(m')=e' 的原始消息 m'。因此,在不要求提供原始消息的验证系统中,此伪造签名将被接受为合法。

    这种漏洞曾被用于伪造身份证明。Craig Wright 曾利用此原理伪造 Satoshi Nakamoto 的签名,试图证明自己是比特币创始人。

  • 建议

    验证签名时必须要求提供原始消息 m,而不仅仅是哈希值。

ECDSA 签名中的 Nonce 侧信道攻击漏洞

  • 严重性

  • 描述

    LadderLeak 是一种存在于 ECDSA 实现中的侧信道漏洞,攻击者可以通过缓存时序分析获取签名过程中使用的随机数 (nonce) 的最高有效位信息,但泄露概率低于 100%(即"不到1比特"的信息)。该漏洞存在于OpenSSL 1.0.2和1.1.0分支以及RELIC工具包0.4.0版本中,特别影响了基于曲线sect163r1和NIST P-192的ECDSA实现。

    该漏洞源于Montgomery梯形算法实现中坐标处理不当导致的微小时间差异。攻击者可以观察到这种时间差异,并利用统计方法推断出随机数k的最高位。即使这种泄露率低于100%(如对P-192为99%,对sect163r1为97.3%),攻击者仍然可以通过改进的Bleichenbacher傅立叶分析方法,收集足够数量的签名后完全恢复私钥。

  • 漏洞示例:

    针对OpenSSL的实现,漏洞表现在两种情况下:

    1. 二进制曲线情况(如sect163r1):

      在函数ec_GF2m_montgomery_point_multiply()中,Montgomery梯形算法第一次迭代时会调用gf2m_Madd()函数进行点加法,当第二MSB为1时,Z1等于x²而不是1,导致模约简操作时间差异。通过缓存时序攻击可以观察到这种差异,正确检测率达到99%。

    2. 素数曲线情况(如NIST P-192):

      在函数ec_mul_consttime()中,根据第二MSB的值,算法会在点加倍运算时处理不同坐标形式的点。当第二MSB为0时,输入点处于仿射坐标,导致BN_copy()函数被调用替代常规的域乘法,产生时间差异。通过缓存时序攻击可以观察到这种差异,正确检测率达到99.53%。

    研究人员利用这种泄露,对P-192实现了完全私钥恢复,只需要约2³⁵个签名;对sect163r1则仅需要约2²⁴个签名。这显著突破了以往攻击技术的限制,以往至少需要2比特泄露才能实现可行的攻击。

  • 建议:

    坐标随机化:

    // 在梯形算法初始化时对Z坐标进行随机化
    void ec_point_set_from_affine(EC_POINT *out, const EC_AFFINE_POINT *in, BN_CTX *ctx) {
        BN_rand(out->Z, BN_num_bits(group->field), -1, 0); // 随机化Z坐标
        BN_mod_mul(out->X, in->x, out->Z, group->field, ctx);
        BN_mod_mul(out->Y, in->y, out->Z, ctx);
    }
    

椭圆曲线密码学中的扭曲曲线攻击漏洞

  • 严重性

  • 描述

    椭圆曲线密码学(ECC)实现中存在一类严重的安全漏洞,称为"扭曲曲线攻击"(twist attacks)。这类攻击利用了椭圆曲线的数学特性和实现中的安全缺陷,允许攻击者在特定条件下提取出受害者的私钥。特别是在使用单坐标梯形算法(如Montgomery梯形算法)实现Diffie-Hellman密钥交换时,如果未采取适当的防御措施,攻击者可能会成功获取私钥信息。

  • 漏洞示例

    扭曲曲线攻击结合了两种密码学攻击方式:

    1. 小子群攻击(small-subgroup attacks):攻击者发送一个小阶点(而非合法的曲线点)作为公钥,诱导受害者使用私钥与之计算,然后通过穷举有限数量的可能性来提取私钥的部分信息。
    2. 无效曲线攻击(invalid-curve attacks):攻击者发送位于不同曲线上的点,利用标准ECC算法实现不验证点是否在正确曲线上的缺陷,获取私钥的额外信息。

    这类攻击尤其危险的原因在于,即使使用单坐标梯形算法限制了无效曲线攻击的范围,攻击者仍可以在原始曲线和其扭曲曲线(twist)上找到小阶点。通过中国剩余定理(CRT),攻击者可以组合对不同模数的私钥信息,最终恢复完整的私钥。

    在许多实际实现中,由于简化代码或提高性能的考虑,开发者可能省略了输入点验证,使系统容易受到这类攻击。特别是当私钥仅选择到ℓ(基点的阶)而不是hℓ(曲线的阶),且未进行原始曲线或扭曲曲线的余因子(cofactor)乘法时,这种风险更加显著。

  • 建议

    验证接收的点是否确实位于预期的曲线上

  • 参考

    https://safecurves.cr.yp.to/twist.html

    https://mp.weixin.qq.com/s/Ivraz1ejoe9UiYYbeQSRoA

3、EdDSA 安全

Ed25519 签名算法中的私钥提取漏洞

  • 严重性

  • 描述

    该漏洞存在于Ed25519签名算法库的实现中,主要由于一些库设计上的缺陷以及用户对算法库接口的错误使用导致。攻击者可以通过操控两次签名过程,使用同一私钥但不同公钥对同一消息进行签名,然后通过分析这两次签名的结果,直接提取出用户的完整私钥。

    在标准实现中,签名计算应该使用与私钥对应的唯一公钥。然而,许多库实现了形如sign(privateKey, message, publicKey)的接口,允许调用者提供任意公钥参数,而没有验证该公钥是否与提供的私钥匹配。

    这一缺陷结合Ed25519签名算法中使用确定性随机数的特性,使得攻击者可以通过两次签名,消除签名过程中的确定性不变量,从而提取出可用于签名的私钥扩展值,进而获取到完整的私钥控制权。

    此漏洞影响所有提供了这种不安全接口且未进行公钥验证的Ed25519实现库,包括可能用于移动端钱包、硬件钱包和云钱包的各种场景。类似的攻击方式也适用于Schnorr签名算法。

  • 漏洞示例

    考虑以下攻击场景:

    1. 攻击者获得对钱包签名接口的访问权限,该接口形如sign(privateKey, message, publicKey)
    2. 攻击者构造两次对同一消息的签名请求:
      • 第一次使用正确的私钥k但替换为攻击者控制的公钥A'
      • 第二次使用相同的私钥k和正确的公钥A
    3. 从数学上看,签名过程如下:
      • 第一次签名:计算S₁ = r + h(R,A',M) · a
      • 第二次签名:计算S₂ = r + h(R,A,M) · a
      • 其中r是确定性生成的随机数,在两次签名中相同
    4. 攻击者获得两个签名后,可以计算:
      • S₁ - S₂ = h(R,A',M) · a - h(R,A,M) · a = (h₁ - h₂) · a
      • 进而解出a = (S₁ - S₂) / (h₁ - h₂)
    5. 一旦攻击者获得a值,就能够对任意消息伪造受害者的签名,完全控制其账户资产
  • 建议

    不要提供形如sign(privateKey, message, publicKey)的接口,应只提供sign(privateKey, message)接口,内部通过私钥计算对应公钥

  • 参考

    https://mp.weixin.qq.com/s/2zvlE8kMPDtwRyQXNEPegg

Ed25519 签名算法在 WolfSSL 中的侧信道漏洞

  • 严重性

  • 描述

    Ed25519 是一种椭圆曲线数字签名算法 (EdDSA),其设计初衷是避免 ECDSA 中必须使用高质量随机数的缺点。Ed25519 通过从消息和辅助密钥确定性地派生临时密钥 (ephemeral key),旨在消除随机数生成的风险。然而,研究表明这种确定性设计在侧信道攻击环境中反而引入了新的漏洞。

    在 WolfSSL 库的 Ed25519 实现中发现了严重的电力分析漏洞,攻击者只需收集约 4,000 个签名操作的电力轨迹,就能完全恢复私钥。漏洞来源于确定性地生成临时密钥过程中使用的 SHA-512 哈希函数,攻击者可以利用差分功耗分析 (DPA) 来提取密钥信息。具体来说,SHA-512 消息调度功能中使用的模加法操作由于其非线性特性产生可观测的侧信道泄漏。

    这一漏洞对使用 WolfSSL 实现 Ed25519 的 IoT 设备和嵌入式系统构成严重威胁,因为这些设备通常更容易受到物理访问和侧信道攻击。

  • 漏洞示例

    攻击实施方法如下:

    1. 攻击者首先测量目标设备执行Ed25519签名操作时的功耗曲线,收集约4,000个不同消息的签名轨迹。

    2. 利用差分功耗分析,攻击者可以提取SHA-512算法中处理密钥信息的中间状态:

      w[16] ← σ1(w[14]) + w[9] + σ0(w[1]) + w[0]
      
      

      其中w[14]和w[9]为已知的消息部分,而w[1]和w[0]为未知的辅助密钥部分。

    3. 攻击者从功耗轨迹中恢复键值k16 = σ0(w[1]) + w[0]、k17、k18和k19,通过分而治之策略,每次攻击8位密钥片段。

    4. 通过解决系统方程组,攻击者可以从这些中间值中恢复完整的辅助密钥b。

    5. 一旦获得辅助密钥b,攻击者就可以计算任意消息的临时密钥r = H(b,M)。

    6. 对于任意现有签名(R,S),攻击者可以计算私有标量a = (S-r)h^(-1) mod l,其中h = H(R,A,M)。

    7. 拥有私有标量a后,攻击者可以伪造任意消息的有效签名。

    实际攻击在32位ARM Cortex-M4F微控制器上实施,该芯片运行WolfSSL 3.10.2的Ed25519实现。攻击完全成功,证明了威胁的现实性。

  • 建议

    修改Ed25519实现,在计算临时密钥时添加一个随机值,使得攻击者无法预测哈希函数的输入:

    r = H(b || R0 || M)  // 其中R0是一个随机值
    

    确保整个第一个1024位数据块仅包含密钥和随机值,而不包含攻击者已知的任何位。

Ed25519 曲线余因子不为 1 引发的双花漏洞

  • 严重性

  • 描述

    Edwards25519 椭圆曲线具有余因子 (Cofactor) 为 8的特性,这意味着曲线上的点群结构包括一个大素数阶子群 G1 和一个阶为 8 的小阶子群 G2。在基于 Edwards25519 构建密码协议(如环签名和RingCT)时,如果未正确处理这种余因子不为1的情况,可能导致严重的双花甚至多花攻击。

    此漏洞源于验证过程中未能确保 key image(用于防止双花的关键组件)属于正确的子群,使得攻击者可以构造特殊的交易,使同一笔资金能被重复花费多次。影响了所有基于CryptoNote 协议且使用 Edwards25519 曲线的加密货币项目。

  • 漏洞示例

    在 Bytecoin 区块链上,攻击者成功利用此漏洞执行了"三花"和"四花"攻击:

    1. 通过选择 G2 子群中的低阶点作为公钥和 key image
    2. 构造特殊的签名参数,使得验证等式在点乘后成立
    3. 使c(签名的一部分)为8的倍数,利用G2子群的特性使 c·P=O 和 c·I=O
    4. 成功制造了四笔花费同一UTXO的交易:
      • cef289d7fab6e35ac123db8a3f06f7675b48067e0dff185c72b140845b8b3b23
      • 7e418cc77935cc349f007cd5409d2b6908e4130321fa6f97ee0fee64b000ff85
      • 5a3db49ef69e1f9dd9b740cabea7328cd3499c29fc4f3295bac3fa5e55384626
      • 74298d301eb4b4da30c06989e0f7ff24a26c90bf4ffc4f2c18f34b7a22cf1136

    这导致 Bytecoin 项目被凭空增发了约 7 亿个 BCN 代币。

  • 建议:

对所有 key image 实施子群成员检查,确保它们属于大素数阶子群G1

// 检查key image是否属于G1
if(ℓ·I != O) {
    // 拒绝该key image
    return false;
}

Ed25519 历史版本中的可延展性漏洞

  • 严重性

  • 描述

    Ed25519 算法本身满足选择消息攻击的安全性,但可延展性问题允许攻击者修改现有签名生成新的有效签名,可能导致假设签名唯一性的系统出现漏洞(如重复验证或篡改检测失败)。在标准RFC 8032中已修复,但旧实现(如ed25519-dalek 1.0.1版本)仍存在风险。

  • 漏洞示例

    在Ed25519签名中,签名由(R, s)组成,其中s是一个标量。如果攻击者获得一个有效签名(R, s)对应消息M和公钥A,他们可以构造新签名(R, s'),其中s' = s + l(l是子群的阶,为素数22522^{252} + 27742317777372353535851937790883648493)。由于群的模运算性质,验证方会验证s' * B == R + h * A(其中h = Sha512(R || A || M),B是基点),这与原签名等价,导致新签名也被接受。例如,如果原s = 123,则s' = 123 + l也会通过验证,但签名不再唯一。

  • 建议

    在签名验证过程中,按照RFC 8032标准添加检查:确保s < l(群的阶)。如果s >= l,则拒绝签名。

  • 参考

    https://mp.weixin.qq.com/s/m5VWfPT-5gfXiUqBeOF_aQ

4、Schnorr 安全

Schnorr Nonce 重用或不良随机数生成

  • 严重性

  • 描述

    Schnorr 签名的安全性高度依赖于随机数(nonce,k)的唯一性和不可预测性。如果在不同签名中重用相同的 nonce,攻击者可以通过数学计算恢复私钥。这种问题在理论上被广泛讨论,但在 Schnorr 签名的实际应用中尚未有公开的案例。

  • 漏洞示例

    虽然不是 Schnorr 签名本身,ECDSA(与 Schnorr 签名有相似数学基础)在比特币和以太坊中有过因 nonce 重用导致私钥泄露的事件。例如,2010 年代早期,一些比特币钱包因随机数生成器缺陷导致 nonce 重用,进而被黑客利用窃取资金。Schnorr 签名在比特币的 Taproot 升级(2021年引入)中开始使用,由于其实现遵循严格的 BIP-340 规范(如确定性nonce生成),目前未见类似问题。

  • 建议

    使用确定性 nonce 生成(如RFC 6979)或加密安全的随机数生成器。

Schnorr 相关密钥攻击

  • 严重性

  • 描述

    2015 年的一篇学术论文分析了 Schnorr 签名和 DSA 在相关密钥攻击下的安全性。研究表明,标准 Schnorr 签名方案在面对 RKA (攻击者可以操纵签名密钥并获取修改后的签名)时不满足完全的 RKA 安全性,存在理论上的攻击方法。例如,攻击者可能通过篡改密钥生成伪造签名。然而,该研究也提出,通过对 Schnorr 签名方案进行轻微修改(如调整签名生成方式),可以实现完全的 RKA 安全性。

  • 漏洞示例

    这不是实际攻击事件,而是理论分析,表明在特定侧信道攻击场景(如篡改设备)下,Schnorr 签名可能面临风险。

  • 建议

    在实现中加入密钥完整性检查或使用抗 RKA 的变种方案。

Schnorr 侧信道攻击

  • 严重性

  • 描述

    理论上,Schnorr 签名的实现可能受到侧信道攻击(如时间攻击、功率分析或电磁泄露)的威胁。这些攻击利用签名生成过程中的物理信息泄露来推导私钥或 nonce。

  • 漏洞示例

    尽管学术界对此有广泛研究,但没有公开的 Schnorr 签名相关侧信道攻击事件。

  • 建议

    实现恒定时间运算、使用抗侧信道的硬件或算法。

5、BLS 安全

Filecoin BLS 签名验证中的可延展性漏洞

  • 严重性

  • 描述

    在 Filecoin 的 Lotus 实现中发现了 BLS 签名验证存在可延展性漏洞。BLS 签名可以以两种不同的形式表示:序列化(serialized)和压缩(compressed),这两种形式都可以通过 BLST 库的VerifyCompressed方法成功验证。但Lotus的区块验证逻辑使用包含签名的区块头CID来识别区块唯一性,这导致了以下安全问题:

    同一个区块如果使用两种不同形式的 BLS 签名,将被视为两个不同的区块,因为它们的 CID 不同。攻击者可以通过提交包含相同内容但签名格式不同的区块来利用这一漏洞,绕过重复区块检测,可能导致区块链分叉、双花攻击或共识故障。

  • 漏洞示例

    以下是攻击场景的示例:

    1. 矿工生成有效区块B,其中包含序列化格式的BLS签名S1
    2. 攻击者可以将此签名转换为压缩格式S2,创建新区块B'
    3. B和B'内容完全相同,仅签名格式不同
    4. 提交这两个区块到网络,由于CID不同,它们将被视为不同区块
    5. 这可能导致:
      • 网络分叉,部分节点接受B,部分接受B'
      • 共识故障检测无法正常工作,因为CID比较判定这是两个不同区块
      • 可能绕过特定交易或区块的验证规则
  • 建议

    在验证签名之前,将所有签名转换为统一格式(要么全部使用序列化格式,要么全部使用压缩格式)

  • 参考

    https://gist.github.com/wadeAlexC/2490d522e81a796af9efcad1686e6754

BLS 库中的零值相关漏洞与"零值分裂"攻击

  • 严重性

  • 描述

    研究人员在四个主流 BLS(Boneh-Lynn-Shacham)加密库和 BLS 标准草案中发现了一系列与零值处理相关的严重安全漏洞,被统称为"splitting zero"(零值分裂)攻击。这些漏洞源于对加密算法中特殊值"0"处理的缺陷,可能导致签名验证绕过、私钥恢复、拒绝服务和其他严重安全问题。

    特别值得注意的是,GitHub 报告还提到了一些额外的零值相关漏洞,包括:

    1. 在supranational/blst库中,零长度签名或零长度消息会导致程序崩溃
    2. 在模p运算中,inverse(0) mod p = 0,但inverse(p) mod p = 1的处理错误

    BLS签名方案因其独特的聚合特性而广泛应用于区块链和分布式系统,这些漏洞可能对依赖这些库的大型系统产生重大安全影响。

  • 漏洞示例

    根据研究文档,"零值分裂"攻击主要包括以下几种类型:

    1. 零签名验证绕过

      攻击者创建一个签名,将特殊的零值作为签名的一部分,利用库中对零值的不正确处理,使无效签名能够通过验证。例如,在某些实现中,点压缩/解压缩算法对零点的特殊处理可能导致验证函数误判零值签名为有效。

    2. 零公钥攻击

      利用对零公钥处理不当的问题,攻击者可以伪造对某些消息的有效签名,或在聚合签名场景中操纵签名结果。

    3. 库崩溃漏洞

      // 发送零长度的签名或消息导致库崩溃
      blst_verify(NULL, 0, message, message_len, ...); // 程序崩溃
      
    4. 模运算中的零值问题

      # 错误实现的模逆元计算
      def inverse_mod(a, p):
          # 缺少对a=0情况的检查
          return pow(a, p-2, p)  # 当a=0时,结果为0而非错误
      
      # 正确情况下应该抛出异常,因为0没有模逆元
      
    5. 聚合签名中的零值操纵

      攻击者可以构造特殊的签名组合,利用零值在聚合过程中的特殊性质,篡改整个聚合签名的结果或使验证过程产生错误的结果。

  • 建议

    明确定义并一致实现对零值(零长度输入、零点、零标量等)的处理策略。

  • 参考

    https://github.com/cryptosubtlety/0

BLS 多重签名中的 Rogue Key Attack 漏洞

  • 严重性

  • 描述

    在 BLS 签名方案中,聚合公钥和签名仅通过简单求和实现。攻击者可以创建“rogue key”(流氓密钥),通过设置秘密密钥为 0 并计算诚实密钥的加法逆来抵消诚实参与者的贡献。

  • 漏洞示例

    1. 生成 rogue public key:(其中 pki 为诚实公钥)。

      pkr=g10pkipk_r=g_1^{0}-\sum pk_i

    2. 伪造签名:使用零密钥签名恶意消息,并计算 rogue signature 以抵消诚实签名。

    3. 此外,攻击者可伪造密钥知识证明(Proof of Possession),通过逆转诚实验明并乘以新索引来绕过 B-KEA 假设。

    这会导致聚合验证通过,但实际为恶意消息,愚弄系统(如 L1 区块链中的恶意块注入)。

  • 建议

    实施 Proof-of-Possession(PoP)机制的严格验证,避免依赖简单求和聚合;考虑使用非线性化聚合或额外随机性。

6、RSA 安全

RSA 密钥长度过小

  • 严重性

  • 描述

    RSA 的密钥长度(通常以位数表示,例如 1024 位、2048 位)决定了 𝑛 的大小,从而影响因子分解的计算复杂度。如果密钥位数过少(如 512 位或更低),攻击者可以使用现代计算资源(如高性能计算机或分布式计算网络)相对容易地分解 𝑛 的因子,进而推导出私钥。

  • 建议

    NIST 建议至少使用 2048 位 RSA 密钥;对于高安全需求,可用 3072 位或 4096 位。

RSA 密钥生成中的近似质数漏洞

  • 严重性

  • 描述

    RSA加密算法的安全性依赖于大整数分解问题的计算困难性,其中模数n是两个大质数p和q的乘积。当p和q选择太过接近时,会产生严重的安全漏洞,使得n的因式分解变得显著容易,从而可能导致RSA私钥被恶意攻击者恢复。

    这一漏洞源于费马分解法(Fermat's factorization method)及其变种算法可以高效地分解两个因子接近的合数。当p和q非常接近时,它们的平均值√(pq)非常接近于(p+q)/2,可以通过尝试(p+q)/2附近的值来进行有效的因式分解。

  • 漏洞示例

    具体来说,如果|p-q|较小,可以定义整数值s = (p+q)/2和d = (p-q)/2,那么p = s+d,q = s-d,且n = pq = s²-d²。这转化为寻找n+d²是否为完全平方数。当p和q接近时,d值较小,使得这种搜索效率非常高。

  • 建议

    遵循NIST标准(如FIPS 186-4),要求|p-q| > 2^(nlen/2-100),其中nlen是模数n的位长;对于2048位RSA密钥,p和q的差值应至少为29242^{924}位;考虑|p-q| > 2^(nlen/2)和|p-(n/p)| > 2^(nlen/2)等更严格的标准;

RSA 模数重用漏洞

  • 严重性

    极高

  • 描述

    RSA加密的安全性基于大整数因式分解问题的困难性,其中模数n是两个大质数p和q的乘积。在正常的RSA实现中,每个密钥对应具有唯一的模数n和相应的唯一公钥指数e与私钥指数d。当模数n被重用时,即使使用不同的公钥指数e和私钥指数d,只要攻击者获得了多个使用相同模数的公钥,就可以通过简单的数学计算恢复私钥。

  • 漏洞示例

    假设有两个不同的RSA密钥对,它们共享相同的模数n,但使用不同的公钥指数e₁和e₂,以及相应的私钥指数d₁和d₂:

    • 密钥对1:(n, e₁, d₁)
    • 密钥对2:(n, e₂, d₂)

    根据RSA的数学原理,我们知道:

    • e₁·d₁ ≡ 1 (mod φ(n))
    • e₂·d₂ ≡ 1 (mod φ(n))

    攻击者知道公钥(n, e₁)和(n, e₂)。通过扩展欧几里德算法,如果e₁和e₂互质,攻击者可以找到整数s和t,使得:

    s·e₁ + t·e₂ = gcd(e₁, e₂) = 1

    这意味着:

    s·e₁ + t·e₂ ≡ 1 (mod φ(n))

    通过数学推导可得:

    s·e₁ ≡ 1 - t·e₂ ≡ 1 - t·(1 - e₂·d₂) ≡ 1 - t + t·e₂·d₂ (mod φ(n))

    这表明,如果t为负数,则d₁ ≡ -t·d₂ + ... (mod φ(n)),攻击者可以直接计算私钥。

  • 建议

    每次生成RSA密钥对时必须生成新的模数n;禁止在不同的密钥对、用户或系统间共享模数;

  • 参考

    https://www.members.tripod.com/irish_ronan/rsa/attacks.html

RSA 小公钥指数漏洞

  • 严重性

  • 描述

    RSA算法的安全性建立在大整数因式分解的计算困难性上,其中公钥由模数n(两个大质数p和q的乘积)和指数e组成,而私钥主要由指数d组成,满足e·d ≡ 1 (mod φ(n))。当选择极小的e值时,特别是e=3,虽然加密操作简化为计算c = m³ mod n,但会引入数学弱点。

  • 漏洞示例

    这类漏洞主要体现在以下几个攻击场景:

    1. 直接开方攻击:若加密消息m较小,导致m³ < n,则加密后的密文c实际上等于m³,攻击者可以直接计算m = ∛c而无需知道私钥。
    2. Håstad广播攻击:当相同消息使用相同的小指数e(如e=3)加密并发送给多个接收者(使用不同的模数n)时,攻击者可以通过中国剩余定理和直接开方恢复原始消息。
    3. Coppersmith相关消息攻击:如果攻击者知道部分明文或多个具有数学关系的消息被加密,可以利用Coppersmith方法在多项式时间内恢复完整消息。
    4. Bleichenbacher填充攻击:针对使用不当填充机制的RSA实现,小指数会加剧这类攻击的有效性。

    这些漏洞在实际应用中尤为危险,因为许多开发者可能为了提高性能而选择小指数,尤其是在资源受限的环境中,且可能不了解或忽视相关的安全风险。

  • 建议

    避免使用e=3、e=5或其他极小值;推荐使用e=65537 (2162^{16}+1),这是一个较大的素数,同时仍保持合理的性能;

RSA 小私钥指数漏洞

  • 严重性

  • 描述

    RSA算法的安全性建立在大整数因式分解问题的计算困难性上,其中公钥由模数n(两个大质数p和q的乘积)和公钥指数e组成,私钥主要由指数d组成,满足ed ≡ 1 (mod φ(n))。私钥指数d用于解密和签名操作,其计算复杂度与d的大小成正比。当使用过小的私钥指数d时,即使模数n非常大,整个加密系统也可能被破解。这种漏洞使攻击者能够在不进行困难的整数因式分解的情况下,通过数学方法恢复私钥,从而完全破坏RSA的安全保证。

  • 漏洞示例

    为了提高解密和签名效率,某些实现可能会刻意选择较小的d值,这会导致几种严重的数学攻击:

    1. Wiener攻击:当d < n^(1/4)/3时,攻击者可以使用连分数展开来找到e/n的良好有理近似,并有效地恢复私钥d。
    2. Boneh-Durfee攻击:这是Wiener攻击的改进版本,当d < n^0.292时可以成功恢复私钥,使用格基规约和Coppersmith方法。
    3. 部分密钥泄露攻击:如果d的部分比特已知,且d较小,攻击者可以更容易地恢复完整的d。
  • 建议

    使用充分大的私钥指数d:确保d至少与φ(n)同等量级,避免刻意选择小的d值。实际上,随机选择e通常会导致d与φ(n)同阶,这是安全的。

RSA 无填充短消息攻击漏洞

  • 严重性

  • 描述

    当使用RSA直接加密消息m时,加密过程计算c = m^e mod n,其中e是公钥指数,n是模数。如果原始消息m相对于模数n非常小(m << n),则m^e可能小于n,导致c = m^e而不是c = m^e mod n。在这种情况下,攻击者可以简单地计算m = c^(1/e)(即对密文c开e次方根)直接恢复原始消息,而无需知道私钥。

  • 漏洞示例

    在RSA加密中,如果不使用填充(如PKCS#1或OAEP)且明文消息m很短(例如,m远小于模数N的平方根),攻击者可以使用Coppersmith定理或其他低指数攻击来恢复m。

    例如:

    • 假设RSA公钥为(N, e),其中e较小(如e=3)。
    • 攻击者截获密文c = m^e mod N,且知道m很短。
    • 通过多项式根求解或格基约简技术,攻击者可以高效找到m,而无需私钥。这在早期或不安全的RSA实现中常见,如一些自定义协议中直接加密短消息(如16字节密钥),导致实际攻击如1990年代的Coppersmith攻击演示。
  • 建议:

    实现RSA加密时使用PKCS#1 v2.1 OAEP(最优非对称加密填充),它添加随机性并扩展消息大小。

RSA 填充预言机攻击漏洞

  • 严重性

  • 描述

    在RSA加密中使用PKCS#1 v1.5填充时,如果服务器在解密无效填充的密文时返回特定错误信息(例如,“无效填充” vs. “其他错误”),这就形成了“填充预言机”。攻击者可以反复发送修改后的密文,观察响应来缩小明文范围。

  • 漏洞示例

    例如:

    • 攻击者截获一个有效的RSA密文c(加密了明文m)。
    • 他们生成变体密文c' = (c * 2^e) mod N,并发送到服务器。
    • 根据服务器是否报告填充有效,攻击者使用Bleichenbacher算法逐步恢复m。这在1998年的Bleichenbacher攻击中被证明有效,曾影响SSL/TLS实现,导致如2014年ROBOT攻击的变体。
  • 建议

    确保RSA解密和填充验证过程在时间上和返回信息上不泄露信息。

RSA 计时攻击漏洞

  • 严重性

  • 描述

    RSA计时攻击是一种侧信道攻击,攻击者通过精确测量RSA操作(如解密或签名)的执行时间差异,能够逐步推导出私钥信息,从而完全破解RSA密码系统。即使RSA算法本身在数学上是安全的,其实现方式可能导致这种严重漏洞。

    这类攻击主要利用了RSA私钥操作中的模幂运算(m^d mod n)通常使用的"平方乘"(square-and-multiply)或"蒙哥马利归约"(Montgomery reduction)等算法。在这些算法中,处理时间会根据私钥d的比特模式而变化。例如,在基本的平方乘算法中,当私钥比特为1时会执行额外的乘法操作,而比特为0时则不会,这导致可测量的时间差异。

  • 漏洞示例

    攻击者通过发送精心选择的消息并测量处理时间,可以推断出私钥d的每一个比特。随着足够多的测量样本,攻击者能够完全重建私钥,从而可以解密所有通信或伪造数字签名。

  • 建议

    确保RSA操作在固定时间内完成,无论私钥比特模式如何。使用原生支持常量时间操作的密码库。

RSA 延展性攻击漏洞

  • 严重性

  • 描述

    RSA加密具有乘法延展性:如果c = m^e mod N,则对于任意k,c' = (c * k^e) mod N 解密为 m * k mod N。攻击者可以利用此特性操纵密文而不触发解密错误。

  • 漏洞示例

    例如:

    • 假设一个加密的银行转账消息m = "转账100元",密文c = m^e mod N。
    • 攻击者计算c' = c * (2^e) mod N,解密后变为"转账200元"。
    • 这在不带签名的协议中有效,如早期电子现金系统或自定义加密方案中,曾在实际攻击中被利用,例如修改加密的投票或金融数据,导致如2010年代的一些区块链或协议漏洞。
  • 建议:

    结合使用数字签名(如RSA-PSS)或消息认证码(MAC)来验证消息完整性,防止篡改。

7、 哈希安全

哈希碰撞生日攻击

  • 严重性

  • 描述

    哈希生日攻击基于概率学中的"生日悖论",利用这一原理可以大幅降低找到哈希碰撞所需的计算复杂度。对于n比特的哈希函数,理论上需要2^n次尝试才能找到特定的碰撞,但利用生日攻击,只需约2^(n/2)次尝试就能找到任意两个输入产生相同哈希值的概率达到50%。

  • 漏洞示例

    假设使用MD5哈希函数(128位输出),攻击者可以通过生成约2^{64}个变体消息来找到碰撞。

    例如:

    • 攻击者创建两个不同合同文件A和B(A是合法的,B是篡改的),使得MD5(A) = MD5(B)。
    • 如果系统使用MD5验证签名,攻击者可以用A的签名伪造B,导致如2004年Flaming攻击中MD5碰撞用于生成假证书,或2012年Flame恶意软件利用碰撞绕过Windows更新验证。
  • 建议

    采用抗碰撞性强的现代哈希算法,如SHA-256、SHA-3或BLAKE2,避免使用MD5和SHA-1等已被证明不安全的算法。

哈希函数长度扩展攻击

  • 严重性

  • 描述

    哈希函数长度扩展攻击(Length Extension Attack)是一种针对使用Merkle-Damgård结构的哈希函数(如MD5、SHA-1和SHA-2系列)的密码学攻击。攻击者利用这类哈希函数的内部工作机制,在知道H(message)和message长度的情况下,无需知道message本身,就能够计算出H(message||padding||extension)的值,其中extension是攻击者选择的任意数据。

    这种攻击之所以可行,是因为Merkle-Damgård结构的哈希算法将输入分割为固定长度的数据块,并且每个块的哈希值依赖于前一个块的哈希状态。这意味着攻击者可以从一个已知的哈希状态继续计算,添加更多数据块,而无需知道产生该状态的原始数据。

    这类漏洞在Web应用、API验证、身份认证系统和区块链应用中尤为危险,特别是当系统使用形如H(secret||message)的简单验证模式时。

  • 漏洞示例

    某项目实现了一个服务端验证机制,用于保护敏感API操作。当用户尝试调用管理功能时,系统要求提供一个哈希值作为验证。验证过程如下:

    1. 用户登录后,服务端使用SHA-256算法计算哈希值:

      hash = SHA-256(SecretKey + "user_id=1&user_name=aa")
      

      其中SecretKey是一个30位的服务端私有密钥,用户不应知道。

    2. 当用户需要执行管理操作时,必须提供相应的参数和正确的哈希值。服务端会重新计算哈希并比对:

      serverHash = SHA-256(SecretKey + data)
      

      如果用户提供的哈希等于serverHash,则验证通过并执行操作。

    攻击者通过以下方式利用此漏洞:

    1. 首先通过正常登录获取合法哈希值:

      hash = "37d310d3465506486431fb2c2eb163f0f470479703f66dc9e5fdead8a3390c68"
      
    2. 使用长度扩展攻击工具,攻击者通过尝试不同长度(最多30次尝试),确定SecretKey的长度。

    3. 一旦确定了正确的长度,攻击者构造新的数据,在原始数据后添加恶意参数"&role=admin":

      newData = "user_id=1&user_name=aa" + padding + "&role=admin"
      

      其中padding是SHA-256算法根据长度添加的填充位。

    4. 攻击者使用长度扩展攻击工具生成新的哈希值,而无需知道SecretKey:

      newHash = "84ae4ae437eeabf3bd8a26294392770b86f64a81998194156ac003d58a21acd0"
      
    5. 攻击者向管理API提交请求,包含构造的数据和计算出的哈希值。服务端验证哈希匹配后,错误地授予攻击者管理权限。

    这种攻击允许攻击者在不知道服务端密钥的情况下,伪造有效的请求并执行未授权的操作,包括权限提升、数据修改或访问敏感功能。

  • 建议

    采用SHA-3、BLAKE2等非Merkle-Damgård结构的现代哈希算法,这些算法天然不受长度扩展攻击影响。

8、AES 安全

AES AES 加密密钥长度不足

  • 严重性

  • 描述

    标准AES支持128位、192位和256位密钥。如果使用的密钥长度过低(例如低于128位,如56位或更低),加密将变得脆弱,攻击者可以通过暴力破解(brute-force attack)或字典攻击等方式快速解密数据。这可能导致敏感信息泄露、数据篡改或系统入侵,尤其在现代计算环境中,高性能GPU和分布式计算可以加速破解过程。

  • 漏洞示例

    根据NIST(美国国家标准与技术研究院)指南,低于128位的密钥长度已被视为不安全,无法抵御当代威胁。

  • 建议

    始终采用至少128位的AES密钥,优先考虑256位以提供更高安全性。避免自定义或低于标准的长度。

AES 中的 IV/Nonce 重用攻击

  • 严重性

  • 描述

    IV/Nonce 重用攻击是针对 AES 加密模式的一种严重安全威胁。当在相同密钥下重复使用初始化向量 (IV) 或一次性数字 (Nonce) 时,会导致严重的安全问题。在 CTR 模式下,重用会产生相同的密钥流,攻击者可以通过密文的 XOR 运算直接获得两个明文的 XOR 结果,进而通过频率分析等方法恢复原始明文。在 GCM 模式下,Nonce 重用不仅会泄露明文信息,还会完全破坏认证机制,允许攻击者伪造认证标签。在 CBC 模式下,虽然影响相对较小,但仍会泄露相同明文块的信息。这种攻击的危险性在于它不需要获取密钥,仅通过观察密文模式就能推断明文内容,在某些情况下甚至能够完全恢复密钥。

  • 漏洞示例

    最典型的案例是 2016 年发现的 Android 加密漏洞,其文件系统加密使用了 AES-CBC 模式但 IV 生成存在缺陷,导致相同文件内容在不同时间加密时可能产生相同的 IV,从而泄露文件模式信息。另一个著名例子是某些 WPA2 实现中的密钥重装攻击 (KRACK),攻击者通过重放握手消息强制重用 Nonce,导致 AES-CCMP 加密流被破解。在 Web 应用中,一些开发者错误地使用固定的 IV 或基于时间戳的可预测 IV 来加密用户数据,导致攻击者能够通过比较不同用户的加密数据来推断敏感信息。云存储服务中也曾出现过类似问题,由于去重机制的设计缺陷,相同文件可能使用相同的加密参数,导致数据泄露。

  • 建议

    应确保每次加密操作都使用唯一且不可预测的 IV/Nonce。

  • 参考

    https://frereit.de/aes_gcm/

AES-ECB 模式弱加密风险

  • 严重性

  • 描述

    电子密码本模式(ECB - Electronic Codebook)是 AES 加密的一种操作模式,存在严重的安全隐患。ECB 模式的主要缺陷在于它对相同的明文块总是生成相同的密文块,而不考虑块在消息中的位置或之前加密的内容。这种确定性的加密模式导致加密后的数据仍然保留了原始数据的模式和结构特征,无法提供现代密码学所要求的语义安全性。

    在 ECB 模式下,密文可能会泄露明文的模式信息,使攻击者能够在不知道密钥的情况下推断部分或全部明文内容。这种弱加密模式尤其不适合加密较大的数据集、结构化数据或包含重复信息的数据。

  • 漏洞示例

    使用 ECB 模式加密图像时,图像的轮廓和主要特征在加密后仍然可见。经典例子是 ECB 企鹅图像,即使经过"加密",企鹅的轮廓依然清晰可辨。

  • 建议

    除非是加密单个块且小于块大小(16字节)的数据,否则应完全避免使用 ECB 模式。

AES-CBC 密文填充攻击

  • 严重性

  • 描述

    AES-CBC 密文填充攻击(Padding Oracle Attack)是一种针对使用 CBC 模式的对称加密系统的侧信道攻击。该攻击利用解密系统在验证填充时泄露的信息,允许攻击者在不知道加密密钥的情况下逐字节解密密文。当加密系统返回关于填充是否有效的任何信息(如错误消息、返回代码或响应时间差异)时,攻击者可以通过有系统地修改密文并观察系统响应,逐步推导出原始明文内容。

    攻击原理基于 CBC 模式的解密特性,当密文被解密时,前一个密文块会与当前解密后的块进行异或操作。通过精心构造并修改密文块,攻击者可以利用填充验证机制作为"Oracle"(预言机),从而推断出解密后的中间值,最终恢复出完整的明文数据。此外,通过 CBC-R(CBC Reverse)技术,攻击者甚至可以伪造能被系统正确解密的密文。

  • 漏洞示例

    历史上多个 TLS 实现受到此类攻击,如 POODLE(2014年)和"幸运十三"(2013年)攻击。攻击者可以通过拦截加密通信,修改密文并分析服务器响应,最终破解加密内容。

  • 建议

    采用 AEAD(带关联数据的认证加密)密码模式,如 AES-GCM 或 ChaCha20-Poly1305;

9、其它密码学协议

弱 Fiat-Shamir 变换

  • 严重性

  • 描述

    Fiat-Shamir 变换是将交互式的零知识证明协议转化为非交互式的证明协议的一个重要方法,它将证明者和验证者中的随机挑战替换成散列函数的输出⁠。冰心漏洞(Frozen Heart)是指在实现 Fiat-Shamir 变换时,使用了"弱 Fiat-Shamir"变换,即只对证明者的部分消息进行散列而不散列公开信息(如参数、公开输入等)。这导致攻击者可以在不知道秘密值的情况下,通过预计算公钥 A 来伪造证明,欺骗验证者。该漏洞影响了多个主流零知识证明系统,包括 Bulletproofs、Plonk、Spartan 和 Wesolowski 的 VDF 等。

  • 建议

    确保在 Fiat-Shamir 变换实现中,将所有公共输入数据也加入到随机数生成过程中。

GG18 和 GG20 Paillier 密钥漏洞

  • 严重性

  • 漏洞描述

    该漏洞存在于两个广泛使用的多方计算(MPC)协议GG18和GG20的规范中,影响了超过10个钱包和库(包括币安托管服务)。漏洞的根源在于协议实现中未检查攻击者的Paillier模数N是否包含小因子或是否为双素数。攻击者可以利用此漏洞与MPC协议中的签名方交互,窃取他们的秘密分片并最终获得主秘密密钥,从而完全提取私钥并盗取加密钱包中的所有资金。[CVE-2023-33241]

    漏洞严重程度取决于实现参数:

    情况1(Beta参数 ~ q^5):攻击者通过恶意构造的消息在MPC协议第一轮中获取私钥分片泄露,仅需16次签名即可恢复完整私钥。Apache Milagro库的特殊子场景中,由于极低的beta参数(256位),攻击者甚至无需构造恶意消息,仅从签名仪式记录中即可恢复密钥。

    情况2(Beta参数 ~ N):攻击者需要主动控制其中一个签名方,通过恶意构造的消息在整个MPC协议交互中获取私钥泄露。对于两方情况,需要20万到10亿次(失败的)签名尝试才能提取完整密钥。

    攻击目标是协议中的"乘法到加法"(Multiplication-to-Addition)阶段,攻击者构造包含16个小素数的恶意Paillier模数,每次攻击提取16位密钥信息,最终通过中国剩余定理恢复完整私钥。

  • 建议

    确保签名方的Paillier公钥经过小素数因子检查,防止攻击者使用包含小因子(如2202^{20}大小的素数)的恶意模数

  • 参考

    https://www.fireblocks.com/blog/gg18-and-gg20-paillier-key-vulnerability-technical-report/