Smart Contract Wallet 파헤치기 시리즈 — Dharma 제 2편

사용자의 소유권을 보장하는 Smart Contract!

--

이번 시리즈는 현재 운용되고 있는 Smart Contract 기반 지갑들을 종합적으로 분석합니다. 기존 블록체인 서비스 사용자의 경험을 기존과 어떻게 다르게 구성하여 개선했는지 알아보고, 어떤 방식으로 Smart Contract를 이용하여 운영하고 있는지 알아보겠습니다.

또한 사용자의 자산이 서비스의 Contract에 예치되는 만큼, 실질적인 자산의 소유주나 권익에 대한 대변을 충분히 하는지 확인할 수 있는 시간이 될 것입니다. 궁극적으로 이러한 아이디어에 영감을 얻어, Ethereum 커뮤니티 내에서 새로운 서비스를 만드는데 도움이 되셨으면 합니다.

<Smart Contract Wallet 파헤치기 시리즈>

<Dharma 제 1편>

<Dharma 제 2편>

<Dharma 제 3편>

🕵️ 지난 파헤치기 이야기

나타난 두 개의 Transaction. 두 번째 Transaction이 “Smart Wallet을 생성하고 Sai를 cSai로 변경”하고 있을 때, 사용자🧑‍🚀는 1원의 수수료도 지불하지 않고 이 모든 상황을 지켜보고만 있는데…

사용자의 계정을 노리는 🦹 해커. 과연 사용자의 계정은 안전할 것인가?

지난 글 이후에 Dharma를 사용해 보셨는지 모르겠습니다. Transaction Fee도 들지 않고, 귀찮은 앱이나 확장 프로그램을 설치 하지 않고 사용할 수 있어서 개인적으로 지갑 대신에 써볼 수도 있겠다. 라는 생각이 들었는데요. 혹시… 이런 생각은 들지 않으셨나요? Coinbase의 계정이 해킹 당한다면? 가뜩이나 Coinbase 자체가 거대한 거래소이다 보니 노리고 있는 해커가 많을 것으로 보이는데 Dharma에 예치된 자산은 안전한가?

Dharma가 Coinbase의 OAuth로 로그인하는 서비스이다 보니, 계정이 탈취당하면 자산 또한 탈취당한 것과 동일하다고 볼 수 있습니다. 이는 기존의 중앙화된 서비스의 단점을 그대로 계승했다고 볼 수 있습니다.

그러나, Dharma는 이런 부분 또한 고려하여 Smart Wallet을 구현하였습니다. 결론적으로 Coinbase의 OAuth가 단순히 수단으로만 전락하게 되는데, 이것이 어떻게 구현되어 있는지 확인 해보도록 하겠습니다.

☔️ 어느 날

Dharma를 사용하던 어느 날, 늘 보던 화면에서 알람이 하나 떠 있는 것을 확인하였고, 이를 눌러보니 아래와 같이 “Take control of your Dharma Smart Wallet”라는 버튼이 하나 생겨 있었습니다.

이 버튼을 보자마자, ‘오 드디어 Wallet의 권한을 사용자에게 주는 건가?’하는 생각이 들었습니다.

이 버튼을 누르자 친절하게도 화면이 하나 떴는데, “절대 자기 소유의 Device에서만 등록”하라는 경고창이 떴습니다.

이때 이를 허용하였고, 일종의 대기하는 팝업이 뜨며, 곧이어 등록되었다라는 알람이 전송 되었습니다.

무슨 앱이나, 확장 프로그램의 설치를 요구할 줄 알았는데, 이렇게 간단히 끝나서 대체 무슨일이 일어난 거지? 하고 제 Wallet의 Transaction들을 살펴보기 시작했습니다.

📮 알 수 없는 Transaction이 도착했다…

아니나 다를까, Smart Wallet의 주소로 알 수 없는 Transaction이 하나 도착해 있었습니다. [Transaction 1]

Transaction 1 Calldata

함수의 이름으로 보았을 때, 사용자의 SigningKey를 등록한다는 것 같습니다. 이전 글에서 보셨다 시피, 사용자의 Wallet으로 할당된 UpgradeBeaconProxyV1로 호출되는 모든 함수는 Dharma의 Wallet 구현체로 Delegatecall을 수행합니다. 그렇기 때문에 setUserSigningKey() 함수의 행방은 배포된 Wallet에서 확인할 수 있을 것입니다.

setUserSigningKey()

함수 자체는 그렇게 어렵게 생기지 않았습니다. userSigningKey를 통해 주소가 하나 들어오고, userSignaturedharmaSignature가 있는 것을 보았을 때 userSigningKey가 EOA인가? 하는 생각이 들었습니다. 내부에서는 Smart Wallet의 nonce와 일치하는 데이터인지 검사하는 부분이 있으며, 실제로 중요한 부분은 _setUserSigningKey 인 것으로 보입니다.

_setUserSigningKey()

아니 속았다…😱

들어오는 주소가 0x0000000000000000000000000000000000000000 인지 검사하고, 내부에 있는 변수에 등록되어 버립니다. 들어온 주소가 별도로 Contract인지 EOA인지 검사하지 않는 것을 보면, 두 가지 데이터 타입을 지원하는 것일 수도 있습니다. 하지만, 우리는 모든 단서를 잃었습니다. 어쩌죠… 무언가 놓쳤던게 있진 않나요?

👓 잊어버린 단서는 없을까?

그렇다면 유일하게 남아 있는 단서는 setUserSigningKey() 함수가 호출되면서 등록된 주소, 0xF8e3c49d7Fe8695a5554f76B699ca75a404a70d7입니다. 어디 한 번 볼까요?

Bingo🔥

다행스럽게도 이 주소는 Contract 였습니다. 우리는 실마리를 잃지 않았습니다! 결과적으로 개인별로 발행된 Smart Wallet에는 이런 Contract가 하나씩 연결되어 있다고 보면 될 것 같습니다! 이 Contract는 Etherscan을 통해 검증된 것은 아니어서, 간단히 🔏KeyRing 이라 부르겠습니다.

저의 🔏KeyRing을 생성한 주소를 보니 DharmaKeyRingFactoryV2에서 newKeyRingAndAdditionalKey() 함수를 실행한 결과물로 만들어진 것이었습니다. [Transaction 2]

Transaction 2 Calldata

함수의 원형을 찾아보면…

newKeyRingAndAdditionalKey()

여기서 좀 특이한 모습을 볼 수 있습니다. 두 번째 인자인 targetKeyRing 을 보면, 이미 배포될 🔏KeyRing의 주소를 미리 알고있다는 점입니다. 이 또한 이전 글에서 보셨다 시피, create2를 이용한 주소 생성 방식을 이용하였습니다.

_deployNewKeyRingIfNeeded() 함수가, 저의 KeyRing을 직접적으로 생성해준 함수일 것입니다.

_deployNewKeyRingIfNeeded()

우선 Assembly Code인 extcodesize를 이용하여, 해당 🔏KeyRing이 이미 배포된 Contract인지 크기 확인을 합니다.

EOA는 별도의 코드가 존재하지 않기 때문에 이 크기가 0으로 되어 있으며, Contract의 경우 크기가 0이상으로 확인됩니다. 이를 통해 이미 배포되어 있는 🔏KeyRing의 경우 별도의 논리적 작동을 이용하게 하려는 것으로 보입니다.

저희의 🔏KeyRing은 아직 코드 상 배포되지 않았기 때문에, 크기가 0일 것이고 첫 번째 예외 문으로 들어갈 것입니다. 와! 지난 글에서 사용했던 패턴들이 여기에 그대로 사용되었습니다. initializationCalldata를 만들고, 새롭게 배포할 Contract에 이를 포함하여 생성하게끔 합니다.

만약 예외의 경우가 발생하였을 때, 지속적으로 실패할 수 있는 Race Condition이 있기 때문에 expectedKeyRing을 그대로 반환하게 됩니다.

위의 코드를 보시면, 🔏KeyRing Contract의 배포 이후에, additionalSigningKey에 대한 추가적인 작업을 수행하는 것을 볼 수 있습니다. 이는 Dharma에서 관리하고 있는 Key로써 추후에 복구 작업이나, Smart Wallet에서 출금을 필요로 할 때 cosigner로 취급됩니다. 간단히 필요한 부분만 살짝 볼까요?

takeAdminAction()

〰️ 4~10번째 줄을 보면 서명을 통해 들어온 정보에 대한 확인을 진행하고 있습니다.

조금 힌트를 드리자면, 앞서 선언된 AdminActionType.AddDualKey의 경우 숫자 6으로 설정되어 있습니다. 따라서 순서대로, adminActionCategory는 0, adminActionKeyCategory는 3, isStandardisAdmin는 각각 true로 설정됩니다.

결과적으로 Admin Key를 추가하는 첫 번째 블록으로 들어오게 되며, uint160으로 변환된 address가 저장되는 것을 확인할 수 있습니다.

🗺 내 진짜 소유권은 어디에?

저는 가장 처음에 모바일 웹 브라우저에서 이를 수행하였습니다. 따라서, 이제 모바일 웹브라우저에서만 Smart Wallet을 사용할 수 있게 되었습니다. 🤨 보안성은 올랐지만, 자유도는 떨어진 것 같습니다.

newKeyRingAndAdditionalKey() 함수에서 등록된 첫 번째 주소인 0x3918339872029921F5C73d330cB79687447FD74d 를 기억 하시나요? 이는 제 웹 브라우저에 저장되어 있는 것으로 확인 되었습니다.

웹 브라우저에는 LocalStorage라는 저장영역이 존재합니다. 이는 각각 Domain과 격리된 저장공간입니다. 또한 웹 브라우저가 종료 되더라도, LocalStorage에 저장된 데이터는 지워지지 않습니다.

Dharma의 경우 이 키가 저장되는 도메인은 https://app.dharma.io/ 입니다. 웹 브라우저의 개발자 도구를 통해 해당 도메인의 LocalStorage를 보면 다음과 같이 나타나는 것을 알 수 있습니다.

이것은 다른 브라우저의 키 입니다 😉

이러한 정보 외에도, 서명을 생성할 수 있는 Private Key와 Salt가 이곳에 저장되어 있습니다. 그렇기 때문에,

🚫🙅‍♀️절대로 다른 사람이 보낸 코드를 개발자 도구에 붙여 넣으시면 안됩니다.🙅‍♂️🚫

이 경우 Smart Wallet의 소유권이 나에게 있지 않을 수도 있다는 점을 유의 하셔야 합니다.

그렇다면, 다른 웹 브라우저에서 Dharma를 이용하려면 비밀 키를 복사해서 다른 브라우저에 넣어야 할까요? 아닙니다. 이 과정 또한 Dharma에서 간단히 만들었습니다.

Approve Your Browser!

다른 브라우저에서 이를 요청하는 경우 옆과 같은 화면이 나타나며, 기존 키가 등록된 브라우저에서 이를 승인해 주어야 합니다.

이를 승인하면 새로운 브라우저에서 자체적으로 비밀키를 생성하고, 주소를 만들어 냅니다. 이후에 주소를 Dharma 서버로 보내어, 🔏KeyRing에 새로운 주소를 등록하게 됩니다.

[Transaction 3]

[Transaction 4]

[Transaction 5]

[Transaction 6]

위의 Transaction 모두 takeAdminAction() 함수를 실행하고 있는 것을 확인할 수 있습니다. 특히나 [Transaction 6]의 경우 위의 개발자 도구에서 본 주소와 동일한 공개키가 등록된 것을 확인할 수 있죠!

간간히 일어나는 일이지만, 사용자가 브라우저 캐시를 삭제하는 과정에서 LocalStorage에 있는 정보가 삭제될 수 있습니다. 아래와 같은 버튼을 통해서요.

용량이 너무 부족하거나, 너무 느린 경우가 아니라면…

그러나 너무 많은 브라우저에 키를 등록하는 것은 장기적으로 보았을 때, 좋은 방법은 아닙니다. 이는 일종의 공격 백터를 늘리는 것으로, 브라우저 또한 해킹당할 수 있는 대상임을 잊어서는 안됩니다!

🥀 Dharma가 없어지면 어쩌지?

브라우저에 저장된 키는 Smart Wallet에서 출금 요청을 할 때 사용됩니다. 브라우저 내부에서 출금에 Sign하고, Dharma는 올바른 웹브라우저의 요청인 경우에만 additionalSigningKey로 Sign되어 출금이 이뤄지게 됩니다.

출금을 요청하는 것 뿐 아니라 Smart Wallet을 만드는 둥, 키를 등록하고 다른 브라우저에서 승인해 주는 것을 보셔서 아시겠지만, 아주 많은 Off-chain 로직이 Smart Contract에 녹아 있는 것을 알 수 있습니다. 예를 들어 Dharma에서 관리하는 EOA. Transaction을 만들어내는 EOA 또한 Dharma의 소유입니다. Smart Contract를 업그레이드 하는 권한을 가진 것 조차 Dharma에서 가지고 있습니다.

만약 Dharma가 서비스를 운영하지 않게 된다면, Smart Wallet 내부에 있는 자산은 어떻게 되는 걸까요?

이러한 사태를 Adharma Contingency라 부릅니다.

Dharma는 주기적으로, DharmaUpgradeBeaconControllerManager 계약에 heartbeat()를 실행합니다. 지금 확인해 보니, 20일 전 한 번 heartbeat를 울린 것으로 확인됩니다.

Dharma가 Heartbeat를 전송하지 않고 최소 90일이 지나면, 누구나 전체 시스템을 Adharma Contingency로 업그레이드 할 수 있다고 합니다. Smart Wallet의 가장 기본적인 기능을 제외하고, 직접 제어할 수 있도록 합니다. 간단히 말해 Dharma 가 없더라도 사용자가 모든 자금을 회수하는 것이 가능해집니다.

따라서 우리는 안심할 수 있게 되었습니다. Dharma가 있든 없든 우리의 자산💰에는 문제가 없다고!

👒 마치며

결과적으로 사용자의 Coinbase의 계정이 탈취 되었어도 웹 브라우저 까지 해킹하여 Private Key를 탈취하는 것은 어렵다는 것입니다. 따라서 OAuth는 Dharma를 이용하기 위한 다양한 수단 들 중 하나가 되었으며 계정이 해킹되었다 하더라도, 키 또한 알고 있어야 하기 때문에 자산의 탈취는 쉽지 않습니다. Blockchain에서 사용되는 암호학은 강력하죠. 💪

개인적으로 생각하기에, 암호학적으로 완벽한 해결책이 있다고 하더라도 이것을 사용하기 어려우면 안된다고 생각합니다. 실제로 Blockchain과 이를 이용해 간단히 만든 서비스들이 그렇습니다. 사용자에게 최악의 경험을 심어 주고, 감흥이 없도록 하는 것이죠.

사용자 경험과 소유권의 보장은 언제나 중요합니다. 더욱이나 Blockchain에서 맞닥뜨린 사용자 경험은 빠르게 진화해야 하며, 누군가를 신뢰해야 하는 나의 자산은 절대 나의 것이 아닐 것입니다. 이러한 서비스를 소개할 수 있고, Smart Contract을 통한 많은 서비스들을 만나볼 수 있었으면 합니다.

다음 글에서는, Smart Wallet의 복구, 그리고 숨겨진 👀 기능, Dharma에서 이용한 다양한 트릭들을 알아볼 것입니다.

[About Us]
HAECHI AUDIT은 글로벌 블록체인 업계를 선도하는 스마트 컨트랙트 보안 감사 및 개발 전문 기업입니다. 다년간 블록체인 기술 연구 개발 경험을 보유하고 있는 전문가들로 구성되어 있으며, 가장 신뢰할 수 있는 스마트 컨트랙트 보안 감사 및 개발 서비스를 제공합니다.

대표적인 포트폴리오로는 SK텔레콤, Kakao 블록체인 자회사인 Ground X, Carry 프로토콜 등이 있으며, 약 50여 곳 이상의 글로벌 프로젝트들을 대상으로 보안 감사를 진행한 경험을 보유하고 있습니다.

또한, 우수한 기술력을 인정받아 삼성전자, 서울시, KB금융그룹, 신한은행, 한화그룹 등의 지원을 받고 있으며, 이더리움 재단으로부터 개발 장려금을 수여한 바 있습니다.

--

--

𝚢𝚘𝚘𝚗𝚜𝚞𝚗𝚐.𝚎𝚝𝚑
HAECHI AUDIT

𝙱𝚕𝚘𝚌𝚔𝚌𝚑𝚊𝚒𝚗 𝚋𝚕𝚊𝚑 𝚋𝚕𝚊𝚑 🍻 𝙾𝚙𝚒𝚗𝚒𝚘𝚗𝚜 𝚊𝚛𝚎 𝚖𝚢 𝚘𝚠𝚗.