EIP-155 단순 재전송 공격 방지 (Simple replay attack protection)
최종 확정 · 보안·프라이버시 · 원문 상태 Final · 2016-10-14 작성
트랜잭션 서명에 체인 고유 번호(체인 ID)를 포함해 한 체인에서 서명한 트랜잭션을 다른 체인에 그대로 다시 제출해도 통하지 않게 만듭니다.
왜 필요한가
이 EIP 이전의 트랜잭션 서명은 논스, 가스 가격, 가스 한도, 수신 주소, 금액, 데이터라는 6개 요소만 해시해서 만들었습니다. 서명 어디에도 어느 체인을 위한 트랜잭션인지 담기지 않았습니다. 그래서 이더리움 메인넷에서 보낸 서명 트랜잭션을 누군가 이더리움 클래식(ETC)이나 Morden 테스트넷 같은 다른 체인에 그대로 복사해 제출하면, 그 체인에서도 유효한 트랜잭션으로 처리될 수 있었습니다. 이것이 재전송 공격(replay attack)입니다. 사용자가 메인넷에서 한 송금이 본인 의사와 무관하게 다른 체인에서도 똑같이 실행될 수 있는 문제입니다. EIP-155는 서명할 데이터에 체인마다 다른 번호인 체인 ID(chain ID)를 넣어 서명이 특정 체인에서만 유효하도록 묶습니다. 원문은 이더리움에서는 동작하지만 ETC나 Morden 테스트넷에서는 동작하지 않는 트랜잭션을 보낼 방법을 제공한다고 설명하고 ETC와 미래의 테스트넷·컨소시엄 체인들도 각자 고유한 체인 ID로 이 방식을 채택하라고 권장합니다.
무엇이 바뀌나
지금
- 서명 대상이 6개 요소(nonce, gasprice, startgas, to, value, data)뿐이라 어느 체인용인지 구분이 없습니다
- 메인넷용 서명 트랜잭션을 ETC나 테스트넷에 그대로 재제출하는 재전송 공격이 가능합니다
- 서명의 v 값은 27 또는 28만 사용합니다
EIP-155 이후
- 서명 대상에 chainid, 0, 0을 더한 9개 요소를 해시해 서명이 특정 체인에 묶입니다
- 다른 체인 ID를 가진 체인에서는 같은 트랜잭션이 유효하지 않습니다
- v 값이 {0,1} + CHAIN_ID * 2 + 35로 바뀌어 체인 ID 정보를 함께 담습니다
- 기존 v = 27, 28 방식 서명도 계속 유효해 하위 호환이 유지됩니다
누구에게 영향이 있나
- 사용자: 메인넷에서 보낸 트랜잭션이 다른 체인에서 몰래 재실행될 위험이 사라집니다.
- 지갑·앱 개발자: 서명 전에 체인 ID를 포함한 9개 요소를 해시하고 새 v 값 규칙을 따라야 합니다.
- 클라이언트 팀: v 값을 보고 새 방식과 기존 방식 서명을 구분해 두 가지 복구 규칙을 모두 처리해야 합니다.
- 테스트넷·다른 체인: 각자 고유한 체인 ID를 정해 같은 방식을 채택하도록 권장됩니다.
심층 설명
서명 데이터가 6개에서 9개 요소로 바뀝니다
block.number >= FORK_BLKNUM이고 CHAIN_ID가 있으면, 서명용 트랜잭션 해시를 계산할 때 기존 6개 RLP 요소 (nonce, gasprice, startgas, to, value, data) 대신 9개 RLP 요소 (nonce, gasprice, startgas, to, value, data, chainid, 0, 0)를 해시해야 합니다(SHOULD). 뒤에 붙는 chainid, 0, 0은 서명 데이터에만 들어가고 실제 전송되는 트랜잭션 필드가 늘어나는 것은 아닙니다. 체인 ID가 해시에 들어가므로 같은 내용의 트랜잭션이라도 체인 ID가 다르면 서명 해시가 달라집니다.
v 값에 체인 ID를 인코딩합니다
9개 요소를 해시해 서명했다면 v는 반드시(MUST) {0,1} + CHAIN_ID * 2 + 35로 설정합니다. 여기서 {0,1}은 secp256k1 서명 과정에서 r이 x 값인 곡선 점의 y 값 패리티입니다. 메인넷(CHAIN_ID = 1)에서는 v가 37 또는 38이 됩니다. 6개 요소만 해시하는 기존 방식을 택했다면 v는 이전처럼 {0,1} + 27로 둡니다. 즉 v 값만 보고도 어떤 방식으로, 어느 체인을 위해 서명했는지 알 수 있습니다.
복구 규칙과 하위 호환
block.number >= FORK_BLKNUM이고 v = CHAIN_ID * 2 + 35 또는 v = CHAIN_ID * 2 + 36이면, 서명자 주소를 복구할 때도 6개가 아니라 9개 RLP 요소를 해시합니다. v = 27, 28을 쓰는 기존 서명 방식은 계속 유효하며 이전과 같은 규칙으로 동작합니다. 새 방식이 SHOULD(권장)로 규정되어 있어 강제 전환이 아니라 점진적 이행이 가능하도록 설계되어 있습니다.
적용 시점과 파라미터
이 EIP는 Spurious Dragon 하드포크(EIP-607)에 포함되어 FORK_BLKNUM = 2,675,000 블록부터 적용됩니다. 메인넷의 CHAIN_ID는 1입니다. 원문의 체인 ID 목록에는 Morden(2, 사용 중단)과 Expanse 메인넷(2), Ropsten(3), Rinkeby(4), Goerli(5), Kovan(42), Geth 사설 체인 기본값(1337)이 실려 있고 더 많은 체인 ID는 chainid.network와 ethereum-lists/chains 저장소에서 확인하고 등록할 수 있습니다.
원문 예시로 보는 실제 값
nonce = 9, gasprice = 20 * 10**9, startgas = 21000, to = 0x3535...3535, value = 10**18, data 빈 값인 트랜잭션의 서명 데이터는 0xec098504a817c800825208943535...018080이고 서명 해시는 0xdaf5a779ae972f972197303d7b574746c7ef83eadac0f2791ad23db92e4c8e53입니다. 개인키 0x4646...4646으로 서명하면 v는 27이 아닌 37이 나옵니다. r, s 값과 최종 서명된 트랜잭션 바이트도 원문 Example 절에 그대로 실려 있습니다.
다른 체인들의 채택을 권장합니다
원문 Rationale은 이 방식이 이더리움에서는 동작하지만 ETC나 Morden 테스트넷에서는 동작하지 않는 트랜잭션을 보낼 수단이 된다고 설명합니다. ETC에는 CHAIN_ID를 다른 값으로 바꿔 이 EIP를 채택하라고 권장하고 미래의 모든 테스트넷, 컨소시엄 체인, 대체 이더리움 체인에도 각자 고유한 값으로 채택하기를 권장합니다. 이 EIP의 상태는 Final로, 명세가 확정되어 더 바뀌지 않습니다.