重入(Reentrancy)
在更新状态之前进行的外部调用,会让攻击者在余额被扣减前重新进入该函数。请遵循 checks-effects-interactions 并使用重入保护。
// vulnerable
function withdraw(uint a) public {
require(bal[msg.sender] >= a);
(bool ok,) = msg.sender.call{value:a}("");
bal[msg.sender] -= a; // too late
}
// fixed
function withdraw(uint a) public nonReentrant {
require(bal[msg.sender] >= a);
bal[msg.sender] -= a; // effects first
(bool ok,) = msg.sender.call{value:a}("");
require(ok);
}
使用 tx.origin 鉴权
用 tx.origin 做授权会让中间合约以用户名义行事。请使用 msg.sender。
预言机 / 价格操纵
从单一资金池读取现货价格会使其可被大额兑换操纵,通常由闪电贷提供资金。优先使用时间加权平均价(TWAP)或聚合预言机。
缺失访问控制
没有限制修饰符的敏感函数可被任何人调用。用 onlyOwner 或角色检查来保护特权函数。
未检查的外部调用
忽略低级调用的返回值会让合约在转账失败后继续执行。务必检查返回的布尔值。
业务逻辑缺陷
反转的比较或错误的份额计算会破坏不变量。这些缺陷是合约特有的,会逃过通用检测器,且往往代价最高。描述预期行为并与代码对比。