章节导读
区块链安全是整个生态系统的基石。从智能合约漏洞到私钥管理,从重入攻击到前端运行(MEV),安全问题无处不在。据统计,2024 年区块链安全事件造成的损失超过 20 亿美元,其中智能合约漏洞占比超过 60%。本章将深入探讨区块链安全威胁模型、智能合约安全最佳实践、审计流程、以及如何构建安全的 DApp。
学习目标:
理解区块链安全威胁模型和攻击向量
掌握智能合约常见漏洞与防御措施
学习智能合约审计流程和工具
了解私钥管理和钱包安全最佳实践
掌握 DApp 前端安全和用户交互安全
学习事件响应和应急处理流程
15.1 区块链安全威胁模型
15.1.1 安全威胁全景图
区块链安全威胁全景图
2024 年区块链安全损失: $2B+,智能合约漏洞占 60%+
🔴 严重威胁
智能合约漏洞:
• 重入攻击 (Reentrancy)
• 整数溢出/下溢
• 未检查的外部调用
• 访问控制缺陷
私钥泄露:
• 明文存储私钥
• 弱随机数生成
• 钓鱼攻击
案例: DAO 黑客 (2016)
损失: $60M ETH
导致以太坊硬分叉
🟠 高危威胁
前端运行 (MEV):
• 三明治攻击
• 抢先交易
• 后运行攻击
闪电贷攻击:
• 价格操纵
• 套利攻击
• 治理攻击
案例: Poly Network (2021)
损失: $611M
跨链桥漏洞
后被黑客归还
🟠 高危威胁
跨链桥安全:
• 验证器妥协
• 消息篡改
• 双花攻击
Oracle 操纵:
• 价格预言机攻击
• 数据源污染
• 延迟攻击
案例: Ronin Bridge (2022)
损失: $625M
5/9 验证器被攻破
史上最大盗窃案之一
🟡 中危威胁
前端安全:
• XSS 攻击
• DNS 劫持
• 假冒网站
依赖漏洞:
• 库漏洞
• 供应链攻击
治理攻击:
• 多数攻击
• 提案操纵
• 投票贿赂
智能合约漏洞 Top 10 (2024)
1. 重入攻击 (Reentrancy) - 占比 18%
TheDAO, Uniswap V1, Cream Finance
2. 访问控制缺陷 - 占比 15%
Parity 多签钱包冻结事件
3. 整数溢出/下溢 - 占比 12%
BeautyChain (BEC) 代币增发
4. 未检查的外部调用 - 占比 10%
5. 前端运行 (Front-Running) - 占比 9%
6. 时间戳依赖 - 占比 8%
7. 其他 (逻辑错误, Gas 限制等) - 28%
防御措施优先级
必须做 (P0):
✓ 代码审计 (至少 2 家独立审计)
✓ 形式化验证关键合约
✓ Bug Bounty 计划
✓ 多签钱包管理权限
推荐做 (P1):
✓ 时间锁 (Timelock) 升级
✓ 紧急暂停机制
✓ 链上监控和报警
可选做 (P2):
✓ 保险覆盖
✓ 去中心化前端托管 (IPFS)
15.1.2 2024 年重大安全事件
时间
项目
损失
漏洞类型
2024.01
Orbit Bridge
$82M
跨链桥验证器妥协
2024.03
Munchables
$62M
内部人员恶意代码 (后归还)
2024.05
Gala Games
$23M
私钥泄露,未授权铸币
2024.07
WazirX
$235M
多签钱包被攻破
2024.09
BingX
$52M
私钥泄露
15.2 智能合约安全最佳实践
15.2.1 重入攻击防御
重入攻击 是智能合约中最常见也是最危险的漏洞之一。
重入攻击原理与防御
❌ 有漏洞的代码
function withdraw(uint amount) public {
require(balances[msg.sender] >= amount);
// ❌ 先转账,后更新状态
(bool success, ) = msg.sender.call{value: amount}("");
require(success);
// ❌ 状态更新在外部调用之后
balances[msg.sender] -= amount;
}
攻击流程:
1. 攻击者调用 withdraw()
2. 合约发送 ETH 到攻击者合约
3. 攻击者 fallback() 再次调用 withdraw()
✅ 安全的代码
function withdraw(uint amount) public {
require(balances[msg.sender] >= amount);
// ✅ 先更新状态 (Checks-Effects-Interactions)
balances[msg.sender] -= amount;
// ✅ 后执行外部调用
(bool success, ) = msg.sender.call{value: amount}("");
require(success);
}
防御措施:
✓ Checks-Effects-Interactions 模式
✓ ReentrancyGuard 修饰器
✓ 拉取模式 (Pull over Push)
OpenZeppelin ReentrancyGuard 实现
contract ReentrancyGuard {
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
modifier nonReentrant() {
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
_status = _ENTERED; // 设置锁
_; // 执行函数
_status = _NOT_ENTERED; // 释放锁
}
}
安全的 withdraw 函数实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; contract SecureBank is ReentrancyGuard { mapping(address => uint256) public balances; event Deposit(address indexed user, uint256 amount); event Withdraw(address indexed user, uint256 amount); function deposit() public payable { balances[msg.sender] += msg.value; emit Deposit(msg.sender, msg.value); } // ✅ 方法 1: Checks-Effects-Interactions 模式 function withdraw(uint256 amount) public nonReentrant { // Checks: 检查条件 require(balances[msg.sender] >= amount, "Insufficient balance"); // Effects: 更新状态 balances[msg.sender] -= amount; // Interactions: 外部交互 (bool success, ) = msg.sender.call{value: amount}(""); require(success, "Transfer failed"); emit Withdraw(msg.sender, amount); } // ✅ 方法 2: Pull over Push (拉取模式) mapping(address => uint256) public pendingWithdrawals; function requestWithdraw(uint256 amount) public { require(balances[msg.sender] >= amount, "Insufficient balance"); balances[msg.sender] -= amount; pendingWithdrawals[msg.sender] += amount; } function withdrawPending() public { uint256 amount = pendingWithdrawals[msg.sender]; require(amount > 0, "No pending withdrawal"); pendingWithdrawals[msg.sender] = 0; (bool success, ) = msg.sender.call{value: amount}(""); require(success, "Transfer failed"); emit Withdraw(msg.sender, amount); } }
15.2.2 访问控制最佳实践
访问控制模式对比
Ownable 模式
适用场景:
• 单一管理员
• 简单权限控制
优点:
✓ 简单易用
✓ Gas 成本低
缺点:
✗ 中心化风险
✗ 单点故障
✗ 无细粒度控制
AccessControl 模式
适用场景:
• 多角色管理
• 复杂权限系统
优点:
✓ 细粒度控制
✓ 角色继承
✓ 可审计
缺点:
✗ 复杂度高
✗ Gas 成本稍高
多签钱包模式
适用场景:
• 高价值资产管理
• DAO 金库
优点:
✓ 去中心化
✓ 安全性高
✓ 防止单点故障
缺点:
✗ 操作复杂
✗ 响应延迟
推荐实践: 分层权限管理
contract SecureProtocol is AccessControl {
bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE"); // 超级管理员
bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE"); // 运营者
bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE"); // 暂停权限
function emergencyPause() public onlyRole(PAUSER_ROLE) {
_pause(); // 任何 PAUSER 可触发
}
function updateCriticalParameter(uint256 newValue) public onlyRole(ADMIN_ROLE) {
// 仅 ADMIN 可修改关键参数
}
AccessControl 完整示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import "@openzeppelin/contracts/access/AccessControl.sol"; import "@openzeppelin/contracts/security/Pausable.sol"; contract SecureVault is AccessControl, Pausable { bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE"); bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE"); bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE"); mapping(address => uint256) public balances; event Deposit(address indexed user, uint256 amount); event Withdraw(address indexed user, uint256 amount); constructor() { // 部署者获得默认管理员角色 _grantRole(DEFAULT_ADMIN_ROLE, msg.sender); _grantRole(ADMIN_ROLE, msg.sender); } // ✅ 分层权限设计 function deposit() public payable whenNotPaused { balances[msg.sender] += msg.value; emit Deposit(msg.sender, msg.value); } // OPERATOR 可执行日常操作 function processWithdrawal(address user, uint256 amount) public onlyRole(OPERATOR_ROLE) whenNotPaused { require(balances[user] >= amount, "Insufficient balance"); balances[user] -= amount; (bool success, ) = user.call{value: amount}(""); require(success, "Transfer failed"); emit Withdraw(user, amount); } // ADMIN 可修改关键参数 function setOperator(address operator, bool enabled) public onlyRole(ADMIN_ROLE) { if (enabled) { grantRole(OPERATOR_ROLE, operator); } else { revokeRole(OPERATOR_ROLE, operator); } } // PAUSER 可紧急暂停 function pause() public onlyRole(PAUSER_ROLE) { _pause(); } function unpause() public onlyRole(ADMIN_ROLE) { _unpause(); } // ✅ 时间锁保护关键操作 uint256 public constant TIMELOCK_DURATION = 2 days; mapping(bytes32 => uint256) public timelocks; function proposeUpgrade(address newImplementation) public onlyRole(ADMIN_ROLE) returns (bytes32) { bytes32 id = keccak256(abi.encodePacked(newImplementation, block.timestamp)); timelocks[id] = block.timestamp + TIMELOCK_DURATION; return id; } function executeUpgrade(bytes32 proposalId, address newImplementation) public onlyRole(ADMIN_ROLE) { require(block.timestamp >= timelocks[proposalId], "Timelock not expired"); require(timelocks[proposalId] != 0, "Proposal not found"); delete timelocks[proposalId]; // 执行升级逻辑 // ... } }
15.2.3 整数溢出防御
Solidity 0.8.0+ 默认开启溢出检查,但仍需注意:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; contract SafeMath { // ✅ Solidity 0.8.0+ 自动检查溢出 function safeAdd(uint256 a, uint256 b) public pure returns (uint256) { return a + b; // 溢出时自动 revert } // ✅ 需要环绕行为时使用 unchecked function wrappingAdd(uint256 a, uint256 b) public pure returns (uint256) { unchecked { return a + b; // 不检查溢出,用于 Gas 优化 } } // ❌ 常见错误:类型转换导致溢出 function dangerousCast(uint256 value) public pure returns (uint8) { // 如果 value > 255,会截断而非 revert return uint8(value); // ❌ 危险! } // ✅ 安全的类型转换 function safeCast(uint256 value) public pure returns (uint8) { require(value <= type(uint8).max, "Value too large"); return uint8(value); // ✅ 安全 } }
15.3 智能合约审计
15.3.1 审计流程
智能合约审计流程
1. 准备阶段
收集需求文档
2. 自动化扫描
工具初筛
3. 手动审查
深度分析
4. 报告
修复验证
准备阶段
文档收集:
• 白皮书
• 架构设计
• 代码仓库
• 测试用例
时长: 1-2 天
成本: $2K-5K
自动化扫描
工具:
• Slither (静态分析)
• Mythril (符号执行)
• Echidna (模糊测试)
• Certora (形式化)
时长: 2-3 天
覆盖率: 40-60%
手动审查
重点检查:
• 业务逻辑漏洞
• 权限管理
• 经济模型缺陷
• Gas 优化
时长: 5-10 天
核心价值所在
报告与修复
漏洞分级:
🔴 Critical (严重)
🟠 High (高危)
🟡 Medium (中危)
🟢 Low (低危)
时长: 3-5 天
修复验证
主流审计公司对比 (2024)
Trail of Bits
成立: 2012 年
审计项目: 500+
工具: Slither, Echidna
价格: $30K-200K+
周期: 2-6 周
客户: MakerDAO, Compound
DeFi Llama, Uniswap
OpenZeppelin
成立: 2015 年
审计项目: 300+
特长: 标准库维护
价格: $25K-150K+
周期: 2-4 周
客户: Aave, Coinbase
TheGraph, 1inch
Certora
成立: 2018 年
特长: 形式化验证
工具: Certora Prover
价格: $40K-250K+
周期: 3-8 周
客户: Balancer, Curve
SushiSwap, Lido
Code4rena (众包)
成立: 2021 年
模式: 竞赛式审计
审计师: 1000+ 安全研究员
价格: $50K-500K
周期: 1-2 周
优势: 多视角
高覆盖率
15.3.2 审计工具使用
Slither 静态分析示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 pip3 install slither-analyzer slither contracts/MyContract.sol slither contracts/ --json slither-report.json slither contracts/ --detect reentrancy-eth,uninitialized-state slither contracts/ --print inheritance-graph
Echidna 模糊测试示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; contract BankTest { mapping(address => uint256) public balances; uint256 public totalDeposits; // ✅ Echidna 不变量测试 function echidna_total_deposits_correct() public view returns (bool) { // 不变量: totalDeposits 应等于所有余额之和 // Echidna 会尝试找到违反此不变量的交易序列 return totalDeposits == address(this).balance; } function deposit() public payable { balances[msg.sender] += msg.value; totalDeposits += msg.value; } function withdraw(uint256 amount) public { require(balances[msg.sender] >= amount); balances[msg.sender] -= amount; totalDeposits -= amount; (bool success, ) = msg.sender.call{value: amount}(""); require(success); } }
1 2 3 4 5 6 7 testMode: assertion testLimit: 10000 seqLen: 100 corpusDir: "corpus" deployer: "0x30000" sender: ["0x10000" , "0x20000" , "0x30000" ]
1 2 echidna-test contracts/BankTest.sol --contract BankTest --config echidna.yaml
15.4 私钥与钱包安全
15.4.1 私钥管理最佳实践
私钥管理方案对比
❌ 不安全方案
明文存储:
• 代码硬编码私钥
• 配置文件明文
• 数据库明文存储
风险:
🔴 代码泄露 = 资产全失
🔴 数据库被攻破 = 灾难
🔴 Git 历史记录泄露
绝对禁止!
✅ 加密存储方案
环境变量 + KMS:
• AWS KMS / Azure Key Vault
• Google Cloud KMS
• HashiCorp Vault
优势:
✓ 加密存储
✓ 访问日志
✓ 权限控制
适用: 生产环境后端
🏆 硬件钱包方案
设备:
• Ledger Nano X
• Trezor Model T
• GridPlus Lattice1
优势:
✓ 私钥永不离开设备
✓ 物理隔离
✓ PIN 码保护
适用: 高价值资产
推荐实践: 分层密钥管理
热钱包 (Hot Wallet)
用途: 日常运营,小额资金
方案:
• 环境变量 + KMS 加密
• 自动化脚本使用
• 每日转账限额
• 实时监控异常交易
资金占比: 5-10%
安全等级: ⭐⭐⭐
冷钱包 (Cold Wallet)
用途: 金库,大额资金
方案:
• 硬件钱包 (Ledger/Trezor)
• 多签钱包 (Gnosis Safe)
• 3/5 或 4/7 签名要求
• 离线签名,在线广播
资金占比: 90-95%
安全等级: ⭐⭐⭐⭐⭐
环境变量 + KMS 示例 (Node.js):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 const PRIVATE_KEY = "0x1234567890abcdef..." ; require ('dotenv' ).config ();const { ethers } = require ('ethers' );const AWS = require ('aws-sdk' );const kms = new AWS .KMS ({ region : 'us-east-1' });async function getDecryptedPrivateKey ( ) { const encryptedKey = process.env .ENCRYPTED_PRIVATE_KEY ; const params = { CiphertextBlob : Buffer .from (encryptedKey, 'base64' ) }; const data = await kms.decrypt (params).promise (); return data.Plaintext .toString ('utf-8' ); } async function sendTransaction ( ) { const privateKey = await getDecryptedPrivateKey (); const wallet = new ethers.Wallet (privateKey); privateKey = null ; const tx = await wallet.sendTransaction ({ to : "0x..." , value : ethers.parseEther ("1.0" ) }); return tx.hash ; } async function logKeyAccess (user, action ) { await cloudwatch.putMetricData ({ Namespace : 'KeyManagement' , MetricData : [{ MetricName : 'PrivateKeyAccess' , Value : 1 , Dimensions : [ { Name : 'User' , Value : user }, { Name : 'Action' , Value : action } ] }] }); }
15.4.2 多签钱包最佳实践
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; // 推荐使用 Gnosis Safe 而非自己实现 // 以下为简化示例说明原理 contract SimpleMultiSig { address[] public owners; uint256 public required; mapping(bytes32 => mapping(address => bool)) public confirmations; mapping(bytes32 => bool) public executed; event Submission(bytes32 indexed txHash); event Confirmation(address indexed sender, bytes32 indexed txHash); event Execution(bytes32 indexed txHash); constructor(address[] memory _owners, uint256 _required) { require(_owners.length > 0, "Owners required"); require(_required > 0 && _required <= _owners.length, "Invalid required"); owners = _owners; required = _required; } // ✅ 提交交易 function submitTransaction(address to, uint256 value, bytes memory data) public returns (bytes32) { bytes32 txHash = keccak256(abi.encodePacked(to, value, data, block.timestamp)); emit Submission(txHash); confirmTransaction(txHash); return txHash; } // ✅ 确认交易 function confirmTransaction(bytes32 txHash) public { require(isOwner(msg.sender), "Not owner"); require(!confirmations[txHash][msg.sender], "Already confirmed"); confirmations[txHash][msg.sender] = true; emit Confirmation(msg.sender, txHash); if (isConfirmed(txHash)) { executeTransaction(txHash); } } // ✅ 执行交易 function executeTransaction(bytes32 txHash) internal { require(!executed[txHash], "Already executed"); require(isConfirmed(txHash), "Not enough confirmations"); executed[txHash] = true; emit Execution(txHash); // 执行交易逻辑 } function isConfirmed(bytes32 txHash) public view returns (bool) { uint256 count = 0; for (uint256 i = 0; i < owners.length; i++) { if (confirmations[txHash][owners[i]]) { count++; } } return count >= required; } function isOwner(address account) public view returns (bool) { for (uint256 i = 0; i < owners.length; i++) { if (owners[i] == account) { return true; } } return false; } }
15.5 DApp 前端安全
15.5.1 前端安全威胁模型
DApp 前端安全威胁与防御
常见威胁
1. 钓鱼网站 (Phishing):
• 假冒官方域名 (typosquatting)
• uniswap.com vs uni5wap.com
2. DNS 劫持:
• 域名解析被篡改
• 指向恶意 IP
3. 恶意交易签名:
• 显示 "Claim Airdrop"
• 实际授权全部代币
防御措施
1. HTTPS + HSTS:
✓ 强制 HTTPS
✓ HSTS Preload 列表
2. 内容安全策略 (CSP):
✓ 禁止内联脚本
✓ 白名单外部资源
3. 交易模拟 (Simulation):
✓ Tenderly / Blocknative
✓ 预览交易结果
前端安全最佳实践
交易确认 UI
必须显示:
✓ 合约地址 (可验证)
✓ 函数名称 (易读)
✓ 参数值 (解码后)
✓ Gas 费用估算
✓ 预期资产变化
示例: Uniswap 交易确认
"Swap 1.5 ETH for ≥ 3000 USDC"
"Price Impact: 0.12%"
代码示例 (React + ethers.js)
// ✅ 交易模拟
const simulateTx = async (tx) => {
const result = await provider.call({
to: tx.to,
data: tx.data,
value: tx.value
});
// 解码结果并展示给用户
return decodeResult(result);
};
前端安全配置示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <meta http-equiv ="Content-Security-Policy" content ="upgrade-insecure-requests" > <meta http-equiv ="Content-Security-Policy" content =" default-src 'self'; script-src 'self' https://cdn.ethers.io; connect-src 'self' https://*.infura.io https://*.alchemy.com; img-src 'self' data: https:; style-src 'self' 'unsafe-inline'; frame-ancestors 'none'; " ><meta http-equiv ="X-Frame-Options" content ="DENY" > <meta http-equiv ="X-XSS-Protection" content ="1; mode=block" >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 import { ethers } from 'ethers' ;interface SafeTransactionRequest { to : string ; data : string ; value : bigint ; expectedFunction : string ; } async function verifyAndSendTransaction ( provider : ethers.Provider , signer : ethers.Signer , txRequest : SafeTransactionRequest ): Promise <ethers.TransactionResponse > { const code = await provider.getCode (txRequest.to ); if (code === '0x' ) { throw new Error ('Target is not a contract' ); } const iface = new ethers.Interface ([ "function swap(uint amountIn, uint amountOutMin, address[] path, address to, uint deadline)" ]); try { const decoded = iface.parseTransaction ({ data : txRequest.data }); if (decoded?.name !== txRequest.expectedFunction ) { throw new Error (`Function mismatch: expected ${txRequest.expectedFunction} , got ${decoded?.name} ` ); } console .log (`Function: ${decoded.name} ` ); console .log (`Args:` , decoded.args ); const simulationResult = await provider.call ({ to : txRequest.to , data : txRequest.data , value : txRequest.value }); console .log ('Simulation Result:' , simulationResult); const tx = await signer.sendTransaction ({ to : txRequest.to , data : txRequest.data , value : txRequest.value }); return tx; } catch (error) { throw new Error (`Transaction verification failed: ${error} ` ); } } async function checkAllowance ( tokenAddress : string , ownerAddress : string , spenderAddress : string , provider : ethers.Provider ): Promise <bigint > { const erc20 = new ethers.Contract ( tokenAddress, ['function allowance(address owner, address spender) view returns (uint256)' ], provider ); const allowance = await erc20.allowance (ownerAddress, spenderAddress); return allowance; }
15.6 应急响应与事件处理
15.6.1 应急响应流程
安全事件应急响应流程
🚨 阶段 1: 发现
(0-5 分钟)
• 监控报警触发
• 用户报告异常
• 链上监控发现
关键指标:
异常交易量 ↑300%
⏸️ 阶段 2: 暂停
(5-15 分钟)
• 触发紧急暂停
• 通知团队
• 冻结可疑账户
执行:
pause() 函数
🔍 阶段 3: 调查
(15 分钟-2 小时)
• 分析攻击向量
• 评估损失规模
• 识别根本原因
工具:
Etherscan, Tenderly
🛠️ 阶段 4: 修复
(2-24 小时)
• 修复漏洞
• 紧急审计
• 部署补丁
验证:
测试网验证
💬 阶段 5: 沟通
(持续)
• 公开事件说明
• Twitter/Discord
• 赔偿方案
原则:
透明、及时
🔄 阶段 6: 恢复
(1-7 天)
• 解除暂停
• 恢复服务
• 监控异常
后续:
事后复盘
紧急暂停机制代码示例
contract EmergencyPausable is Pausable, AccessControl {
bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
function emergencyPause() external onlyRole(PAUSER_ROLE) {
_pause(); emit EmergencyPaused(msg.sender, block.timestamp);
}
完整的应急暂停合约示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import "@openzeppelin/contracts/security/Pausable.sol"; import "@openzeppelin/contracts/access/AccessControl.sol"; contract EmergencyProtocol is Pausable, AccessControl { bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE"); bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE"); event EmergencyPaused(address indexed by, uint256 timestamp, string reason); event EmergencyUnpaused(address indexed by, uint256 timestamp); event SuspiciousActivity(address indexed user, string activity, uint256 amount); mapping(address => bool) public blacklisted; // ✅ 监控阈值 uint256 public constant MAX_DAILY_VOLUME = 1000 ether; mapping(uint256 => uint256) public dailyVolume; // day => volume constructor() { _grantRole(DEFAULT_ADMIN_ROLE, msg.sender); _grantRole(ADMIN_ROLE, msg.sender); _grantRole(PAUSER_ROLE, msg.sender); } // ✅ 紧急暂停 (任何 PAUSER 可触发) function emergencyPause(string memory reason) external onlyRole(PAUSER_ROLE) { _pause(); emit EmergencyPaused(msg.sender, block.timestamp, reason); } // ✅ 恢复 (仅 ADMIN) function unpause() external onlyRole(ADMIN_ROLE) { _unpause(); emit EmergencyUnpaused(msg.sender, block.timestamp); } // ✅ 黑名单管理 function addToBlacklist(address user) external onlyRole(ADMIN_ROLE) { blacklisted[user] = true; } function removeFromBlacklist(address user) external onlyRole(ADMIN_ROLE) { blacklisted[user] = false; } // ✅ 示例业务函数 - 带异常检测 function transfer(address to, uint256 amount) external whenNotPaused { require(!blacklisted[msg.sender], "Address blacklisted"); uint256 today = block.timestamp / 1 days; dailyVolume[today] += amount; // 检测异常交易量 if (dailyVolume[today] > MAX_DAILY_VOLUME) { emit SuspiciousActivity(msg.sender, "Excessive daily volume", dailyVolume[today]); _pause(); // 自动触发暂停 } // 执行转账逻辑 // ... } // ✅ 链上监控查询 function getDailyVolume() external view returns (uint256) { uint256 today = block.timestamp / 1 days; return dailyVolume[today]; } }