1. 先建模,再写防护
安全不是“加几个 modifier”,而是先回答三个问题:
- 资产从哪里进、从哪里出
- 谁有权调用关键函数
- 攻击者最值钱的攻击路径是什么
这就是威胁建模。
2. 常见漏洞与对策
| 漏洞类型 | 典型表现 | 防护建议 |
|---|---|---|
| 重入攻击 | 外部调用前后状态不一致 | CEI、nonReentrant |
| 权限绕过 | 漏加权限检查 | 角色模型 + 单测覆盖 |
| 价格操纵 | 直接信任瞬时价格 | TWAP / 多源预言机 |
| 签名重放 | nonce/域分离缺失 | EIP-712 + nonce |
| 升级风险 | 实现合约可被恶意升级 | 多签 + Timelock + 审计 |
| 回调攻击 | hook/callback 可重入 | 白名单 + 重入保护 + 限流 |
3. 安全版提现示例
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";
import {Pausable} from "@openzeppelin/contracts/utils/Pausable.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
contract SafeVault is AccessControl, Pausable, ReentrancyGuard {
bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
mapping(address => uint256) public balanceOf;
event Deposited(address indexed user, uint256 amount);
event Withdrawn(address indexed user, uint256 amount);
constructor(address admin) {
_grantRole(DEFAULT_ADMIN_ROLE, admin);
_grantRole(PAUSER_ROLE, admin);
}
function deposit() external payable whenNotPaused {
require(msg.value > 0, "ZERO_AMOUNT");
balanceOf[msg.sender] += msg.value;
emit Deposited(msg.sender, msg.value);
}
function withdraw(uint256 amount) external whenNotPaused nonReentrant {
require(amount > 0, "ZERO_AMOUNT");
require(balanceOf[msg.sender] >= amount, "INSUFFICIENT_BALANCE");
// Checks + Effects
balanceOf[msg.sender] -= amount;
// Interaction
(bool ok, ) = msg.sender.call{value: amount}("");
require(ok, "ETH_TRANSFER_FAILED");
emit Withdrawn(msg.sender, amount);
}
function pause() external onlyRole(PAUSER_ROLE) {
_pause();
}
function unpause() external onlyRole(PAUSER_ROLE) {
_unpause();
}
}
4. 2026 生态下的新风险面
- Uniswap v4 hooks 可插拔逻辑:回调链路更复杂,需特别关注外部调用顺序与授权边界。
- ERC-4337 / EIP-7702 账户路径:签名验证、nonce 域分离、赞助交易结算都要单独测试。
- 跨链桥与消息层:目标链执行失败、重放、消息乱序是高发问题。
5. 推荐安全流水线
forge test -vvv
forge test --match-test invariant -vvv
forge coverage
再配合:
- 静态分析(如 Slither)
- 形式化规则(关键模块)
- 第三方审计
- 漏洞赏金计划
6. 上线前强制检查单
- 关键资产函数已覆盖失败路径测试
- 权限已移交多签/治理,不在个人私钥
- 合约参数(费率、限额、地址)可追溯
- 预言机、回调、外部依赖有降级方案
- 监控告警与紧急暂停流程已演练
安全不是某个库,而是一整套工程纪律。