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

作者:慢雾 
链接:https://mp.weixin.qq.com/s/0C7LMVFjc6nPtyQdDpXaHQ
声明:本文为转载内容,读者可通过原文链接获得更多信息。如作者对转载形式有任何异议,请联系我们,我们将按照作者要求进行修改。转载仅用于信息分享,不构成任何投资建议,不代表吴说观点与立场。


背景概述
 
密码学是 Web3 安全的基石,从私钥的生成到交易的签名,每一个环节都依赖于密码学组件的正确与安全实现。然而,在 Web3 项目的实际开发中,研发团队往往聚焦于业务逻辑与性能优化,对底层密码学实现的细微之处缺乏足够的警惕。使用不安全的随机数生成器、错误地调用密码学库、或对算法特性的理解偏差,都可能引入致命漏洞,导致私钥泄露、签名伪造、进而造成无法挽回的资产损失。
 
通常,项目方会依赖安全审计来发现此类问题。然而,审计作为一种事后验证手段,难以覆盖项目全生命周期。许多漏洞根植于开发阶段初期选型与设计,若团队自身不具备基础的密码学安全知识,则无法从根源上规避风险。
 
因此,慢雾安全团队开源了区块链应用中常见的密码学风险,希望将一线攻防中积累的、关于密码学误用的典型案例与最佳实践沉淀下来。我们期望项目方团队能够在开发之初便筑牢密码学安全防线,最终建立起自主、持续的安全编码能力。
 
 
本文仅罗列部分内容,完整内容首发于 GitHub,欢迎大家到 GitHub 上 Watch、Fork 及 Star:https://github.com/slowmist/Common-Cryptographic-Risks-in-Blockchain-Applications
 

0x01 私钥随机数安全

 

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


  • 严重性:


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


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


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


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


  • 严重性


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


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


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


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


  • 严重性:


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


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


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


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


  • 严重性:


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


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


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


  1. Libbitcoin Mersenne Twister 弱熵漏洞


  • 严重性:


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


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


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


6. OpenSSL 随机数生成器安全风险


  • 严重性:


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


  • 漏洞示例:低熵密钥泄露(LESLI),即攻击者通过获取 nonce 并通过暴力枚举所有可能的 PIN 码和 RNG 状态,可以恢复原始的 PIN 值。

     

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

     

0x02 ECDSA 安全

 

1. secp256r1 后门问题


  • 严重性:


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


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


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


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


  • 严重性:


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


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


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


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


  • 严重性:


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


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


4. 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 社区采用),拒绝非规范签名以消除可锻造性。


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


  • 严重性:


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


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


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


  • 严重性:


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


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


7. 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 的实现,漏洞表现在二进制曲线情况(如 sect163r1)和素数曲线情况(如 NIST P-192) 


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


    • 严重性:


    • 描述:在 Filecoin 的 Lotus 实现中发现了 BLS 签名验证存在可延展性漏洞。BLS 签名可以以序列化(serialized) 和压缩(compressed) 这两种不同的形式表示,这两种形式都可以通过 BLST 库的 VerifyCompressed 方法成功验证。但 Lotus 的区块验证逻辑使用包含签名的区块头 CID 来识别区块唯一性,这导致了以下安全问题:同一个区块如果使用两种不同形式的 BLS 签名,将被视为两个不同的区块,因为它们的 CID 不同。攻击者可以通过提交包含相同内容但签名格式不同的区块来利用这一漏洞,绕过重复区块检测,可能导致区块链分叉、双花攻击或共识故障。


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


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


    • 严重性:


    • 描述:研究人员在四个主流 BLS (Boneh-Lynn-Shacham) 加密库和 BLS 标准草案中发现了一系列与零值处理相关的严重安全漏洞,被统称为"splitting zero"(零值分裂)攻击。这些漏洞源于对加密算法中特殊值"0"处理的缺陷,可能导致签名验证绕过、私钥恢复、拒绝服务和其他严重安全问题。特别值得注意的是,GitHub 报告还提到了一些额外的零值相关漏洞,包括:在 supranational/blst 库中,零长度签名或零长度消息会导致程序崩溃;在模 p 运算中,inverse(0) mod p = 0,但 inverse(p) mod p = 1 的处理错误。BLS 签名方案因其独特的聚合特性而广泛应用于区块链和分布式系统,这些漏洞可能对依赖这些库的大型系统产生重大安全影响。


    • 漏洞示例:根据研究文档,“零值分裂”攻击主要包括零签名验证绕过、零公钥攻击、库崩溃漏洞、模运算中的零值问题,以及聚合签名中的零值操纵。
       

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


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


    • 严重性:


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


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

       

    0x06 RSA 安全

     


    1. 哈希碰撞生日攻击


    • 严重性:


    • 描述:哈希生日攻击基于概率学中的"生日悖论",利用这一原理可以大幅降低找到哈希碰撞所需的计算复杂度。对于 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 等已被证明不安全的算法。


    2. 哈希函数长度扩展攻击


    • 严重性:


    • 描述:哈希函数长度扩展攻击(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) 的简单验证模式时。


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

       

    0x08 AES 安全

     


    1. 弱 Fiat-Shamir 变换


    • 严重性:


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


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


    2. GG18 和 GG20 Paillier 密钥漏洞


    • 严重性:


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


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

       

    总结

     

     

    慢雾安全团队开源——区块链应用中常见的密码学风险,旨在系统性地揭示私钥生成、数字签名、哈希函数及对称加密等核心环节中潜藏的高危安全风险。Web3 项目方可以参考本文对各类漏洞的原理剖析、利用场景及修复建议,深化对密码学安全的理解,规避常见的实现陷阱,从而更有效地保障项目和用户的资产安全。



Nguồn
Tuyên bố từ chối trách nhiệm: Nội dung trên chỉ là ý kiến của tác giả, không đại diện cho bất kỳ lập trường nào của Followin, không nhằm mục đích và sẽ không được hiểu hay hiểu là lời khuyên đầu tư từ Followin.
Thích
Thêm vào Yêu thích
Bình luận