WTF Solidity 合约安全: S04. 权限管理漏洞
我最近在重新学 Solidity,巩固一下细节,也写一个“WTF Solidity 合约安全”,供小白们使用(编程大佬可以另找教程),每周更新 1-3 讲。
所有代码和教程开源在 github: github.com/AmazingAng/WTF-Solidity
这一讲,我们将介绍智能合约的权限管理漏洞。这个漏洞导致了跨链桥 Poly Network 被黑 6.11 亿美元,并导致了BSC上DeFi项目 ShadowFi 被黑 $300,000。
权限管理漏洞
智能合约中的权限管理定义了不同角色在应用中的权限。通常来说,代币的铸造、提取资金、暂停等功能都需要较高权限的用户才能调用。如果权限配置错误,就可能造成意想不到的损失。下面我们介绍两种常见的权限管理漏洞。
1. 权限配置错误
如果合约中特殊功能没有加上权限管理,那么任何人都能铸造大量代币或将合约中的资金提光。跨链桥 Poly Network 的合约中转移守护者的函数没有配置相应权限,被黑客改为自己的地址,从而提走了合约中的 6.11 亿美元。
在下面的代码中,mint()
函数没有进行权限管理,那么任何人都可以调用它铸造代币。
// 错误的mint函数,没有限制权限
function badMint(address to, uint amount) public {
_mint(to, amount);
}
![](/assets/images/S04-1-3dcfc0682f4a7d1a3c3e4dd54dadda19.png)
2. 授权检查错误
另一类常见的权限管理漏洞是没有在函数中检查调用者是否拥有足够的授权。BSC上DeFi项目 ShadowFi 的代币合约忘了在 burn()
销毁函数中检查调用者的授权额度,导致攻击者可以任意的销毁其他地址的代币。在黑客将流动性池子中的代币销毁之后,仅需卖出一点代币就可以将池子里的所有 BNB
提走,获利 $300,000。
// 错误的burn函数,没有限制权限
function badBurn(address account, uint amount) public {
_burn(account, amount);
}
![](/assets/images/S04-2-795724a7471724958f8e8933e2c71aff.png)
预防办法
权限管理漏洞主要有两种预防办法:
使用 Openzeppelin 的权限管理库给合约的特殊函数配置相应的权限,比如使用
OnlyOwner
修饰器,只有合约所有者才能调用。// 正确的mint函数,使用 onlyOwner 修饰器限制权限
function goodMint(address to, uint amount) public onlyOwner {
_mint(to, amount);
}在函数的逻辑中确保合约调用者拥有足够的授权。
// 正确的burn函数,如果销毁的不是自己的代币,则会检查授权
function goodBurn(address account, uint amount) public {
if(msg.sender != account){
_spendAllowance(account, msg.sender, amount);
}
_burn(account, amount);
}
总结
这一讲,我们介绍了智能合约中的权限管理漏洞。它主要有两种形式:权限配置错误和授权检查错误。为了避免这类漏洞,我们要使用权限管理库给特殊函数配置相应的权限,并且在函数的逻辑中确保合约调用者拥有足够的授权。