返回论坛

Solidity 安全深度剖析(三):函数可见性漏洞与伪随机数陷阱

查找币 漏洞披露 安全研究 Web3安全 区块链安全

查找币安全研究院

钱包恢复评估 | 链上取证分析 | Web3 事件响应
以合法授权、证据保全、隐私保护和可复核流程为前提,不要求用户在线提交完整私钥或助记词。

查看研究院 研究报告中心
**查找币安全团队技术分享系列** --- ## 一、引言 在智能合约开发中,看似微小的代码疏忽可能导致数百万美元的损失。本文将深入剖析两个典型的安全隐患:**函数可见性设置不当**与**伪随机数生成(PRNG)缺陷**,并通过真实案例揭示其背后的攻击原理与防御策略。 --- ## 二、函数可见性:默认的“公开”陷阱 ### 2.1 可见性机制概述 Solidity 为函数提供了四种可见性修饰符: - `public`:任何地址均可调用 - `private`:仅限当前合约内部调用 - `internal`:当前合约及派生合约可调用 - `external`:仅允许外部调用 **关键风险点**:函数的默认可见性为 `public`。这意味着开发者若未显式指定可见性,函数将对全网用户开放调用权限。 ### 2.2 漏洞示例:被“公开”的私密函数 ```solidity contract HashForEther { function withdrawWinnings() { // 要求地址最后8位十六进制为0 require(uint32(msg.sender) == 0); _sendWinnings(); } function _sendWinnings() { msg.sender.transfer(this.balance); } } ``` **攻击分析**: - `_sendWinnings()` 函数本应为 `private` 或 `internal` - 因未指定可见性,默认变为 `public` - 任何攻击者可直接调用 `_sendWinnings()` 窃取合约余额 ### 2.3 防御策略 1. **显式声明所有函数可见性**,即使意图为 `public` 2. **启用编译器警告**:最新 Solidity 版本会对未声明可见性的函数发出警告 3. **规范命名约定**:建议对 `private` 函数使用前缀 `_` 增强辨识度 ### 2.4 真实案例:Parity 多签钱包首次攻击 **损失金额**:约3100万美元(三个钱包被盗) **攻击原理**: - 多签钱包依赖于 `WalletLibrary` 库合约 - 库合约中的 `initMultiowned()` 函数**未指定可见性** - 攻击者通过该函数重新初始化钱包所有权 ```solidity contract WalletLibrary is WalletEvents { // 危险:未指定可见性 function initMultiowned(address[] _owners, uint _required) { m_numOwners = _owners.length + 1; m_owners[1] = uint(msg.sender); // ... 所有权设置逻辑 } } ``` **技术细节**: - 攻击者调用 `initMultiowned()` 将自己设为所有者 - 随后调用 `execute()` 函数转走资金 - 该漏洞暴露了**库合约设计**中可见性管理的根本缺陷 --- ## 三、伪随机数生成(PRNG):区块链上的确定性困境 ### 3.1 核心矛盾 区块链是**确定性系统**,而博彩、抽奖等场景需要**不可预测的随机性**。这构成了智能合约安全的核心挑战。 ### 3.2 常见错误:使用区块变量作为熵源 开发者常误用的“随机”变量: - `blockhash` - `block.timestamp` - `block.number` - `block.gaslimit` **致命缺陷**:这些变量均可被矿工操纵。 ### 3.3 攻击场景模拟 假设一个轮盘赌合约逻辑: ```solidity if (blockhash(block.number - 1) % 2 == 0) { // 返回黑色 } else { // 返回红色 } ``` **矿工攻击路径**: 1. 矿工在黑色上下注100万美元 2. 挖出新区块后检查哈希值 3. 若哈希值为奇数,**拒绝发布该区块** 4. 继续挖矿,直到获得偶数哈希区块 5. 发布区块并赢得赌注(假设区块奖励+手续费 < 100万美元) **倍增攻击**:同一区块内的所有交易共享相同伪随机数,攻击者可通过批量交易放大收益。 ### 3.4 防御策略 | 方案 | 原理 | 适用场景 | |------|------|----------| | **承诺-揭示机制** | 用户先提交哈希承诺,再揭示原始值 | 小规模、信任型应用 | | **RanDAO** | 多参与方共同生成随机数 | 去中心化应用 | | **预言机** | 引入链外随机数源 | 高安全性需求场景 | **核心原则**:**区块变量不应作为熵源**,随机性必须来自区块链外部。 ### 3.5 真实案例:PRNG 合约漏洞分析 **研究员**:Arseny Reutov **统计范围**:3649份使用PRNG的智能合约 **发现结果**:43份合约存在可被利用的漏洞(占比1.18%) **漏洞特征**: - 使用 `blockhash` 作为随机种子 - 未考虑同一区块内交易的时间顺序 - 缺乏防止矿工操纵的保护机制 --- ## 四、综合安全建议 1. **可见性管理**: - 所有函数必须显式声明可见性 - 库合约函数需特别审查 - 使用静态分析工具检测默认可见性 2. **随机数生成**: - 避免使用任何链上变量作为熵源 - 优先采用预言机或承诺-揭示方案 - 对博彩类合约进行形式化验证 3. **开发规范**: - 遵循“最小权限原则” - 启用 Solidity 编译器所有安全警告 - 定期进行第三方安全审计 --- ## 五、结语 函数可见性漏洞和伪随机数缺陷是智能合约安全领域的两大经典陷阱。前者暴露了**代码可见性管理**的疏忽,后者揭示了**区块链确定性本质**与**随机性需求**之间的根本矛盾。 通过本文的案例分析,我们再次强调:**安全不是功能,而是设计**。每一次函数声明、每一行随机数逻辑,都应当经过严格的安全审查。 --- **本文由查找币安全团队整理发布** *参考资源* - Solidity 官方文档:函数可见性说明 - Parity 多签钱包漏洞分析(Haseeb Qureshi) - PRNG 合约安全研究(Arseny Reutov) - RanDAO 去中心化随机数协议
在论坛中查看和回复