Smart Contract analyzer

_Block·2022년 6월 22일
0

SmartContract

목록 보기
4/5
post-thumbnail

Smart Contract는 테스트 하기 굉장히 어렵습니다.

왜냐하면 코드가 수정이 되면 새로 배포를 해야하고, 이전의 값을 가지고 있는 형태가 아니기 때문에 이러한 부분에서 많은 어려움이 있습니다.

  • 물론 Proxy구조를 띄고 있고, 구조적으로 작성이 잘 되어 있다면 큰 문제는 없습니다.
  • 물론 기존의 변수명, 변수 추가를 하는데에는 굉장히 조심해야하는 문제도 있습니다.

그러기 떄문에 truffle, hardHat과 같은 Tool을 활용 하고는 하며, 이후 어느정도 테스트가 진행이 되었을때에는

Web과 연동하여 Contract를 테스트 합니다.

하지만 SmartContract가 로직대로 동작을 하는것과 보안적으로 우수하냐에 대해서는 다른 개념입니다.

Solidity가 어려운 언어는 아니며, 그러기 때문에 누구나 쉽게 로직 작성이 가능하지만

보안적으로 우수하냐에 대해서는 쉽게 접근을 하지 못합니다.

  • 예를들면 리앤트런을 방지하는 코드 작성이나,
  • 함부로 require문에 == 키워드를 작성하지 않아야 한다는 조건이나.

간단하게 이론적으로는 이해하지 못하는 코드 방식으로 작성을 해야 하는 경우가 몇몇 있습니다.

  • 본인 또한 이러한 부분을 간과하여 코드를 수정중에 있습니다.

이러한 문제점이 야기되는 이유와, 간단하게 리앤트런을 방지하는 방법, 마지막으로 SmartContract가 보안적으로 문제가 없는지 분석하는 방법에 대해서 다루어 보겠습니다.

🐾 리앤트런

SmartContract는 기본적으로 큐 구조로 동작을 하게 됩니다.

즉 들어오는 트랜잭션을 순차적으로 처리를 하고,

DB와 다르게 기존의 값을 수정하였다가 롤백을 하는 방식으로 동작을 합니다.

위 내용이 이해가 어려울수 있기 떄문에 추가 설명을 하자면

DB같은 경우에는 트랜잭션이 일어나면 DB의 값을 수정하고, 트랜잭션 과정중 오류가 발생을 하면 다시 롤백을 하는 방식으로 동작을 하지 않습니다.

만약 A라는 데이터에게 트랜잭션을 보내 수정을 하고자 한다면

DB는 A-A라는 새로운 간접 데이터를 만들어 내고, 그후 해당 간접 데이터를 수정 합니다.
그러다가 트랜잭션이 실패하면 A-A라는 데이터를 버리는 형태로 동작을 하고 성공을 한다면 A-A를 A에 적용하는 방식으로 동작을 합니다.

하지만 SmartContract는 좀 다릅니다.

A라는 데이터를 수정을 하다가 트랜잭션이 실패하면 다시 A를 롤백시키고, 성공하면 성공된 데이터를 반영하는 방식으로 동작 합니다.

이러한 방법이 하나의 Contract에서 msg.sender를 사용하였을 떄에는 한명만 접근이 가능하다는 장점이 있습니다.

하지만 만약 하나의 컨트랙트에 같은 User가 다른 트랜잭션으로 접근이 가능하면 어떻게 할까요?

이런 상황은 이와 같습니다.

contract A {

	mapping(address => uint256) private addMoney;
	
    // addMoney에는 모두 충분한 금액이 있다고 가정
	function withDraw() public {
    
    	require(addMoney[msg.sender] != 0);
        
    	msg.sender.transfer(addMoney[msg.sender]);
        
        addMoney[msg.sender] = 0;
    }
}

이러한 코드가 있습니다.

단순히 A라는 컨트랙트에서만 이 코드를 동작 시킨다면 문제가 되지 않습니다.

하지만 만약 fallback을 통해서 A컨트랙트를 호출하면 어떻게 될까요?

  • 이전 글에서 보았던 Proxy같은 형태를 통한 호출을 말합니다.

물론 대다수의 경우에는 실패하는 하는 코드가 되겠지만 동시에 호출을 하게 되면 특정 트랜잭션이 성공하기 전에 다른 트랜잭션이 발생하여 withDraw의 require문을 통과하는 경우가 생길 수 있습니다.

addMoney[msg.sender] = 0 이라는 코드가 동작하기 전에

fallback을 통해서 호출하면 해당 상태값은 0이 되지 않았기 때문에 withDraw가 한번 더 실행이 되는 것 입니다.

그러기 떄문에 저희는 token을 전송하는 로직에는 반드시 functionLock을 실행 시켜주어야 합니다.

contract A {

	mapping(address => uint256) private addMoney;
    mapping(address => bool) private functionLock;
    
    modifier funcLock() {
    	require(!functionLock[msg.sender]);
        functionLock[msg.sender] = true;
        _;
        functionLock[msg.sender] = false;
    }
	
    // addMoney에는 모두 충분한 금액이 있다고 가정
	function withDraw() public funcLock{
    
    	require(addMoney[msg.sender] != 0);
        
    	msg.sender.transfer(addMoney[msg.sender]);
        
        addMoney[msg.sender] = 0;
    }
}

이와 같이 withDraw가 실행되기 전에 modifier부터 실행을 시켜 msg.sender라는 User가 function이 종료되기 전에 또 함수를 실행하지 못하게 방지하는 방식으로 해당 공격을 막아 낼수 있습니다.

오해 할 수 있는 부분을 추가 설명을 하자면

Solidity는 큐 형태로 동작을 하는 것이 맞습니다.

하지만 이러한 큐 형태가 전체 네트워크에 구성이 되어 있는 것이 아니라

SmartContract하나에 큐의 형태로 동작을 하기 떄문에

fallback으로 동작시키는 SmartContract와 그냥 contract A에 보내는 트랜잭션은 다른 큐에 쌓이게 되며

이러한 이유로 해당 공격이 가능 합니다.

🐾 require문에 ==, != 금지

굉장히 단순하게 생각을 하여 조건을 걸었을떄에는 대부분 == 키워드를 사용합니다.

contract A {

	uint256 private myNum;
	
	function chck(uint256 _num) {
    	require(myNum != 3);
        
        myNum += _num;
    }
}

이런식으로도 활용을 하며, 특정 상황을 막는 것을 의미합니다.

하지만 앞서 말한 리앤트런 공격이 일어나면 상황은 조금 다릅니다.

앞선 상황과 같이 해당 함수가 실행이 되기 전에 동시에 트랜잭션을 유발하여 두번의 트랜잭션이 통과 하게 구현이 가능하기 떄문에

이러한 부분을 고려하여 작성이 되어야 합니다.

  • 이 부분은 좀더 검색을 하던가 바로 다음에 다뤄볼 tool을 통해서 알아 볼수 있습니다.

🐾 Smart Contract analyzer

말 그대로 해당 SmartContract가 보안적으로 부족한 부분은 없는지 취약점을 검사 할수 있는 FramWork입니다.

기본적으로 Python으로 동작이 가능하면 자세한 동작 방법은 해당 링크를 확인하면 됩니다.

Python으로 가능하지 따로 Python을 작성할 필요는 없으며 단순한 명령어를 통해서 동작이 가능합니다.

저 같은 경우에는 python 3.6.5버전을 설치하여 사용하였습니다.

단순히 검증만을 할 경우에는 slither tests/uninitialized.sol, 또는 slither . 명령어를 활용해도 좋지만

해당 명령어는 사용자 친화적으로 동작하지 않기 떄문에 좀더 내 코드가 어느정도의 위험성이 있는지를 확인하고자 한다면

slither file.sol --print human-summary 키워드를 사용하면 됩니다.

원하는 상황에 맞춰서 사용을 하면 됩니다.

직접 사용하면 알 수 있는 부분이기 떄문에 이러한 FramWork에 대해서는 이런것이 있다 정도만 다루며 글을 마무리 하겠습니다.

감사합니다.

profile
Block_Chain 개발자 입니다. 해당 블로그는 네트워크에 관한 내용을 다루고 있습니다.

0개의 댓글