Smart Contract 취약점 [6] Bad Randomness

Bad Randomness

Randomness라는 성질은, 이더리움 내부에서는 언급하기 상당히 조심스러운 성질이다.

외부에 모든 데이터가 공개되어 있다는 특징 때문에 Random이라는 값이 예층 가능하게 될 수도 있다.

 

말로는 설명하기 까다롭기 때문에, 총 2가지 예시와 함께 살펴보자.

uint256 private seed;

function play() public payable {
	require(msg.value >= 1 ether);
	iteration++;
	uint randomNumber = uint(keccak256(seed + iteration));
	if (randomNumber % 2 == 0) {
		msg.sender.transfer(this.balance);
	}
}

 

위 코드를 보면, play 함수를 호출하면, private 값인 seed와 카운터 값인 iteration을 더한 값이 keccak256 해시를 거쳐 randomNumber가 만들어진다. 이 때, randomNumber가 짝수일 경우 돈을 받게되는 것이다.

그런데, private seed라고 해도, 블록체인 내부의 변수는 모두 읽어올 수 있기 때문에 모두가 읽는 것이 가능하다. 이는 iteration 값도 마찬가지다.

즉, 현재 상태를 보고 randomNumber이 짝수가 되는 경우에만 play함수를 호출한다면 항상 play에서 승리하여 돈을 가져오는 것이 가능하다.

 

또다른 예시를 살펴보자.

function play() public payable {
	require(msg.value >= 1 ether);
	if (block.blockhash(blockNumber) % 2 == 0) {
		msg.sender.transfer(this.balance);
	}
}

block.blockhash는 Solidity에서 random number를 생성할 때 사용하는 함수이다. 이 때, 입력으로 blockNumber가 들어가게 되는데, blockNumber부터 총 256개의 블록의 데이터를 이용하여 Hash 결과를 만들어낸다. 

 

이 때, blockNumber이 "최근 256개의 블록"의 number가 아니라면 어떨까?

 

그렇게 되면 block.blockhash의 결과가 Hash가 아닌, 예측 가능한 값이 되어버린다.

예를 들어, 현재 총 1000개의 블록이 있다고 가정하고 blockNumber가 10이라고 하자. 원래 의도대로라면 매번 함수를 호출할 때 마다 block.blockhash(blockNumber)의 값이 변해야 하지만, 변하지 않고 고정된 값이 되어버린다.

 

 

 

이처럼, Solidity 내부에서는 Random값을 필요로 하는 Smart Contract가 되게 많고, Random 값을 만드는 로직도 다양하지만, 과연 진짜 Random한 값인지 잘 생각하고 사용해야 한다.

 

 

 

 

 

 

참고

https://dasp.co/#item-6

  Comments,     Trackbacks