[마스터링 이더리움] 스마트 컨트랙트 보안 - 패턴10 레이스 컨디션
레이스 컨디션 (Race Condition)
- 경쟁조건: 사용자들이 코드 실행을 경쟁한다.
- 예를 들어 채굴의 nonce 찾기와 같이 솔루션을 먼저 제출하고자 하는 경쟁.
- 프런트 러닝 (front running), 누가 먼저 코드를 실행시키나? nonce 문제 해결. 다른 사람보다 앞서서 코드를 실행. 트랜잭션이 블록에 포함되도록 한다.
- 재진입성은 경쟁조건의 예이다.
취약점
이더리움 노드는 트랙잰션 풀에서 트랙잰션을 선택하여 블록을 생성한다.
트랜잭션은 채굴자가 nonce 문제를 풀고 채굴에 성공해야지만 유효하게 된다.
중요한 것은 채굴자가 어떤 트랜잭션을 블록에 포함할지 결정한다는 것이다.
이 때, 채굴자는 트랜잭션에 할당된 gasPrice가 높은 것을 우선적으로 처리한다.
그런테 해커가 트랜잭션 풀에서 nonce 문제를 풀 수 있는 트랜잭션을 찾아낼 수 있다.
해커는 이 트랜잭션에서 데이터를 얻어서 gasPrice만 더 높여서 트랜잭션을 생성하고, 자신의 트랜잭션이 채굴되도록 할 수 있다. (트랜잭션을 동일하게 구성하고, 오로지 gasPrice만 높여서 트랜잭션 생성)
FindThisHash 컨트랙트
1 contract FindThisHash {
2 bytes32 constant public hash =
3 0xb5b5b97fafd9855eec9b41f74dfb6c38f5951141f9a3ecd7f44d5479b630ee0a;
4
5 constructor() public payable {} // load with ether
6
7 function solve(string solution) public {
8 // If you can find the pre-image of the hash, receive 1000 ether
9 require(hash == sha3(solution));
10 msg.sender.transfer(1000 ether);
11 }
12 }
- 특정 해시를 만드는 문자열을 찾는 것.
- 사용자는 예상 솔루션을 파라미터로 하여 solve 함수 호출. 해답이라면 이더를 받는다.
- 해커는 이 사용자가 제출하는 값을 볼 수 있을 수 있다.
- 먼저 제출한 값이 솔루션인지, 검증하고, 동일한 트랜잭션을 생성할 수 있다. 물론 gasPrice를 크게 해서 말이다.
- 해커가 채굴자와 다를 것이라는 생각은 하지 않는게 좋다.
예방기법
- gasPrice의 상한을 두어, 해커가 gasPrice를 높여서 먼저 채굴되지 않도록 할 수 있다.
- 이렇게 하더라도 채굴자가 해킹을 한다면, 순서를 임의로 조정할 수 있다.
- 다른 방법은 commit-reveal 방식을 사용하는 것이다.
- 이것은 사용자가 트랜잭션을 전송할 때, 감춰진 정보(해커도 볼 수 없는 정보)를 추가하여 전송하는 것이다. 트랜잭션이 채굴되어 블록에 기록될 때, 사용자는 다시 트랜잭션을 보내서 감줘친 정보를 드러나게 한다.
- 이렇게 하면, 앞서 얘기한 프런트 러닝을 막을 수 있다(솔루션이 탈취되는 문제)