引入 nonce:每次登录或交易签名都携带唯一 nonce;
链 ID 检查:在签名中加入特定链 ID,防止跨链重放;
使用 EIP-712 签名结构:结构化签名防止签名滥用。
从不把私钥写入前端代码或硬盘明文保存;
后端钱包使用冷钱包或 HSM 服务(如 AWS KMS, Fireblocks);
使用环境变量加密存储助记词或私钥,部署中使用 .env + vault
;
限制私钥操作权限,冷热钱包分离;
签名前显示明确信息说明(如“登录授权而非转账”);
使用 EIP-712 结构化数据签名,避免用户签不明数据;
前端签名提示加上网站来源(如 yourapp.com
)+ 描述;
Front-running 是攻击者监听 mempool,抢在用户交易之前提交相同或对其有利的交易(如抢先购买 NFT、调整价格、抢矿);
使用 commit-reveal 策略:
用户先提交加密数据(commit),后提交明文(reveal);
延迟执行 + 随机性机制:
引入链上随机数,避免固定执行顺序;
Flashbots:
将交易发送至 Flashbots 私有池,避免泄露到公共 mempool;
签名授权后端执行:
用户授权后由后端发送交易,缩短暴露时间窗口;
Merkle Tree 空投(常用):
构造用户地址+数量的 Merkle Root,存入合约;
用户自行调用 claim()
提供 Merkle proof;
✅ 优点:一次部署即可支持大规模空投,节省 Gas;
✅ 防止重复领取,通过记录 claimed[address] = true
;
签名式空投:
后端为每个地址签名分发信息;
用户提交签名至合约,合约验证签名;
适合灵活分发、动态设置领取者;
Batch Transfer + 权限限制:
批量发送空投(transferBatch()
);
控制可操作账户,避免私钥被盗后滥发;
空投逻辑使用开源库(如 OpenZeppelin MerkleDistributor);
空投合约部署后不可修改 Merkle Root;
添加空投截止时间、防多次领取、防重入等措施;
实现方式:使用 delegatecall
将调用转发至逻辑合约;
典型模式:
Transparent Proxy(OpenZeppelin)
UUPS Proxy(更轻量,推荐)
模式 | 优点 | 缺点 |
---|---|---|
Transparent | 稳定,OpenZeppelin支持 | 存储布局要求严格,结构复杂 |
UUPS | 更节省 Gas,可自定义升级逻辑 | 升级函数自身易被攻击,需审慎授权 |
新部署一个合约,人工迁移数据;
✅ 优点:无 delegatecall 安全风险;
❌ 缺点:迁移成本高、用户需重新授权;
将状态变量独立放在一个固定合约;
易维护,但结构复杂、不推荐新项目使用;
漏洞类型 | 示例代码 / 描述 | 防御方式 |
---|---|---|
重入攻击 | 调用外部合约前修改状态(如提现) | Checks-Effects-Interactions;使用 ReentrancyGuard |
整数溢出/下溢 | uint256 a = b - c(未判断 b > c) | 使用 SafeMath 或启用 Solidity ≥0.8 自动检查 |
无访问控制 | 没有限制 setOwner() |
添加 onlyOwner 或 AccessControl |
调用未初始化合约地址 | 外部合约地址为空调用 | require 地址非 0 且合约存在 |
delegatecall 滥用 | 调用非可信合约逻辑代码 | 限制 delegatecall 使用,仅可信模块 |
Gas limit 不足 | 数组遍历转账 gas 用尽,状态不变 | 加限制、使用 pull 模式而非 push |
tx.origin 验证身份 | require(tx.origin == owner) 易被 phishing |
用 msg.sender 替代 tx.origin |
总结建议:
使用 OpenZeppelin 工具、Hardhat 插件审计合约;
引入 CI 检查(如 slither
、mythx
);
所有用户交互都应防御重入、钓鱼、溢出、权限越权;
升级合约需严格测试、控制 upgrade 权限;
尽量使用 audited 合约库,避免“自己写轮子”;