SWC-107/重入漏洞
调用外部合约的主要风险之一是外部合约可以接管控制流程。在重入攻击(也称为递归调用攻击)中, 恶意合约会在函数的第一次调用完成之前回调发起调用的合约,这可能导致函数的不同调用以预期之外 的方式进行交互。
CWE漏洞分类
整改方案
避免重入漏洞的最佳实践是:
- 确保在执行外部调用之前已经更新了所有的内部状态,这一模式被成为:Checks-Effects-Interactions
- 使用可重入锁,例如 OpenZeppelin的ReentrancyGuard
参考文献
合约示例
Modify_reentrancy.sol
pragma solidity ^0.5.0;
contract ModifierEntrancy {
mapping (address => uint) public tokenBalance;
string constant name = "Nu Token";
Bank bank;
constructor() public{
bank = new Bank();
}
//If a contract has a zero balance and supports the token give them some token
function airDrop() hasNoBalance supportsToken public{
tokenBalance[msg.sender] += 20;
}
//Checks that the contract responds the way we want
modifier supportsToken() {
require(keccak256(abi.encodePacked("Nu Token")) == bank.supportsToken());
_;
}
//Checks that the caller has a zero balance
modifier hasNoBalance {
require(tokenBalance[msg.sender] == 0);
_;
}
}
contract Bank{
function supportsToken() external returns(bytes32) {
return keccak256(abi.encodePacked("Nu Token"));
}
}
Modify_reentrancy.yaml
description: Reentrancy (state change after external call)
issues:
- id: SWC-107
count: 1
locations:
- bytecode_offsets: {}
line_numbers:
modifier_reentrancy.sol: [14, 15]
Modify_reentrancy_fixed.sol
pragma solidity ^0.5.0;
contract ModifierEntrancy {
mapping (address => uint) public tokenBalance;
string constant name = "Nu Token";
Bank bank;
constructor() public{
bank = new Bank();
}
//If a contract has a zero balance and supports the token give them some token
function airDrop() supportsToken hasNoBalance public{ // In the fixed version supportsToken comes before hasNoBalance
tokenBalance[msg.sender] += 20;
}
//Checks that the contract responds the way we want
modifier supportsToken() {
require(keccak256(abi.encodePacked("Nu Token")) == bank.supportsToken());
_;
}
//Checks that the caller has a zero balance
modifier hasNoBalance {
require(tokenBalance[msg.sender] == 0);
_;
}
}
contract Bank{
function supportsToken() external returns(bytes32){
return(keccak256(abi.encodePacked("Nu Token")));
}
}
Modify_reentrancy_fixed.yaml
description: Reentrancy (state change after external call)
issues:
- id: SWC-107
count: 0
locations: []
simple_dao.sol
/*
* @source: http://blockchain.unica.it/projects/ethereum-survey/attacks.html#simpledao
* @author: Atzei N., Bartoletti M., Cimoli T
* Modified by Josselin Feist
*/
pragma solidity 0.4.24;
contract SimpleDAO {
mapping (address => uint) public credit;
function donate(address to) payable public{
credit[to] += msg.value;
}
function withdraw(uint amount) public{
if (credit[msg.sender]>= amount) {
require(msg.sender.call.value(amount)());
credit[msg.sender]-=amount;
}
}
function queryCredit(address to) view public returns(uint){
return credit[to];
}
}
simple_dao.yaml
description: Reentrancy (state change after external call)
issues:
- id: SWC-107
count: 1
locations:
- bytecode_offsets:
'0xe85040f3e719fc3c0e490a0134d2e8daffadf2d2b7f011336f95505f8d9a92f8': [648, 655]
line_numbers:
simple_dao.sol: [17, 18]
simple_dao_fixed.sol
/*
* @source: http://blockchain.unica.it/projects/ethereum-survey/attacks.html#simpledao
* @author: Atzei N., Bartoletti M., Cimoli T
* Modified by Bernhard Mueller, Josselin Feist
*/
pragma solidity 0.4.24;
contract SimpleDAO {
mapping (address => uint) public credit;
function donate(address to) payable public{
credit[to] += msg.value;
}
function withdraw(uint amount) public {
if (credit[msg.sender]>= amount) {
credit[msg.sender]-=amount;
require(msg.sender.call.value(amount)());
}
}
function queryCredit(address to) view public returns (uint){
return credit[to];
}
}
simple_dao_fixed.yaml
description: Reentrancy (state change after external call)
issues:
- id: SWC-107
count: 0
locations: []