본문 바로가기

자주나오는패턴

[Common Vulnerabilities] Code With No Effects

솔리디티에서 어떠한 영향도 없는 코드를 작성하는 것은 가능하다. 현재 솔리디티 컴파일러는 영향 없는 코드에 대한 경고를 리턴하지 않을 것이다. 또한 메시지 call을 하고 리턴 값을 확인하지 않으면 call이 실패하거나 공격자가 실패를 강제로 call할 수도 있다. 그럴 경우 예기치 못한 문제가 생길 수 있다. 만약 low-level의 call 메서드를 사용한다면 fail, success 등의 리턴 값을 명확하게 해야 한다. 

예를 들어서 msg.sender.call{value: address(this).balance}("") 코드에서 ""를 빼먹기 쉽다. 

그러한 것들을 예방하기 위해서는 유닛 테스트코드를 작성해서 코드의 동작에 대한 검증을 진행해야한다.

 

    // 잘못된 코드
    function withdraw(uint amount) public {
        require(amount <= balance[msg.sender], 'amount must be less than balance');

        uint previousBalance = balance[msg.sender];
        balance[msg.sender] = previousBalance - amount;

        // Attempt to send amount from the contract to msg.sender
        msg.sender.call.value(amount);
    }

 

이렇게 했을 때는 balance매핑에서 유저의 잔액만 감소하고 유저에게 amount가 전송되지 않는다.

 

따라서 아래와 같이 수정을 해야 한다.

	//after solidity ver 0.7.0 
	//맞게 수정된 코드
    function withdraw(uint amount) public {
        require(amount <= balance[msg.sender], 'amount must be less than balance');

        uint previousBalance = balance[msg.sender];
        balance[msg.sender] = previousBalance - amount;

        // Attempt to send amount from the contract to msg.sender
        (bool success, ) = msg.sender.call{value:amount}("");
        require(success, 'transfer failed');
    }

 

call함수가 잘 동작했는지 return값을 받아서 처리를 해야 한다.

 

 

[ 참고 및 추가자료 ]

https://swcregistry.io/docs/SWC-104/

https://swcregistry.io/docs/SWC-135/

https://consensys.github.io/smart-contract-best-practices/development-recommendations/general/external-calls/#handle-errors-in-external-calls