> THE-ARSENAL
> _

우리는 모두 경계선 위에서 살아간다. 현실과 디지털, 공적인 자아와 사적인 자아, 신뢰하는 영역과 의심하는 영역 사이에서. 디지털 세계는 특히나 이 경계들의 총합으로 이루어져 있다. 견고한 성벽으로 둘러싸인 시스템 내부에, 우리는 스스로 또 다른 작은 격리 구역을 만들곤 한다. 안전하다고 믿는 핵심 시스템과, 위험할지도 모른다고 여기는 외부의 코드를 분리하는 것이다. 이것은 지극히 합리적이고 안전한 접근 방식이다. 모든 보안 아키텍처는 이 분리의 원칙 위에 세워진다. 문제는, 이 완벽하게 분리된 두 세계가 서로 대화해야만 하는 순간에 발생한다.

성벽 안의 왕국과 성벽 밖의 샌드박스가 서로 손을 잡아야 할 때, 우리는 그 둘을 잇는 아주 좁고 위험한 다리를 놓아야 한다. 이 다리를 우리는 게이트웨이라 부른다. 그리고 모든 비극은 언제나 이 다리 위에서 시작된다. 보안의 본질은 가장 강한 성벽을 쌓는 것이 아니라, 가장 취약한 다리를 어떻게 설계하고 방어하느냐에 달려있다. 어떤 시스템은 이 위험한 다리를 가장 복잡하고도 아슬아슬한 방식으로 구현하고 있다. 그 구조를 들여다보는 것은, 잘 설계된 시스템이라도 단 하나의 잘못된 가정, 단 하나의 논리적 오류가 어떻게 전체를 무너뜨릴 수 있는지 보여주는 완벽한 사례 연구다.

네이티브 프레임워크(NF)라는 신뢰받는 왕국

블록체인 시스템의 심장부에는 신뢰받는 왕국이 있다. 우리는 이것을 네이티브 프레임워크(NF)라고 부른다. 이는 시스템의 기축 통화와 규칙이 정의된 코어 모듈이다. 이 왕국은 절대적인 힘을 가지고 있다. 네이티브 자산, 예를 들어 시스템의 기축 코인을 발행(Mint)하고 소각(Burn)할 수 있는 유일한 권한을 갖는다. 사용자의 자산을 이 주소에서 저 주소로 옮기는 모든 기록을 최종적으로 승인한다. 시스템의 모든 규칙을 관장하는 거버넌스의 결정을 집행한다. 이 왕국은 스스로 은행이자 조폐국이다.

이 왕국의 코드는 신성불가침으로 여겨진다. 그것은 시스템을 만든 설계자들이 직접 작성하고, 수많은 감사를 거쳤으며, 체인의 모든 참여자가 이 코드가 정직하게 작동할 것이라고 합의한 결과물이다. 이곳은 모든 권한이 집중된, 신뢰의 근원이다. 특히, 사용자가 자신의 네이티브 자산을 VM에서 사용하기 위해 변환하고자 할 때, 이 네이티브 자산은 이 왕국의 특정 주소, 즉 모듈 계좌에 안전하게 잠금(Lock) 처리된다. 이 계좌는 시스템의 모든 '진짜' 자산을 보관하는 거대한 금고, 트레저리(Treasury)가 된다. 이 금고의 안전은 시스템의 안전과 동일한 의미를 갖는다. 이 왕국의 코드는 신뢰받는 언어로 작성되어 있으며, 그 실행은 가장 빠른 속도와 가장 높은 권한으로 이루어진다. 이 왕국의 코드는 단 한 순간도 통제를 벗어나서는 안 된다.

'가상 머신'(VM)이라는 격리된 샌드박스

반면, 이 왕국은 외부인들에게 특정 공간을 내어준다. 바로 가상 머신(VM)이라는 영역이다. 가장 대표적인 것이 이더리움 가상 머신(EVM)이다. 이곳은 왕국에 거주하는 시민들이나 외부의 방문객들이 자신들만의 계약서(스마트 컨트랙트)를 자유롭게 올리고 실행할 수 있는, 일종의 자유 시장이다. 왕국이 이곳을 허용한 이유는 단 하나, '혁신'이다. 왕국이 모든 규칙을 직접 만들 수는 없기에, 사용자들이 스스로 금융 상품을 만들고, 게임을 개발하고, 데이터를 교환할 수 있는 공간을 제공함으로써 생태계를 확장하려는 것이다.

하지만 이 자유에는 치명적인 대가가 따른다. 왕국은 이 VM에 올라오는 코드를 전혀 신뢰하지 않는다. 이 코드는 익명의 개발자가 작성했을 수도 있고, 고의적인 악성 코드가 숨겨져 있을 수도 있다. 이 신뢰할 수 없는 코드가 왕국의 신성한 금고에 접근하거나, 네이티브 자산을 마음대로 발행하도록 내버려 둘 수는 없다. 따라서 왕국은 이 VM을 '격리된 샌드박스' 안에 가둔다. 이 샌드박스는 완벽하게 통제된다. 이 안의 코드는 외부 인터넷에 접근할 수 없으며, 모든 연산은 100% '결정론적'이어야 한다. 그리고 모든 연산에는 '가스'라는 비용이 부과되어, 무한 루프 같은 코드가 시스템 전체를 마비시키는 것을 막는다. 이 VM은 왕국과는 완벽히 분리된, 힘도 없고 권한도 없는, 오직 자신에게 주어진 작은 모래 상자 안에서만 작동하는 통제된 공간이다. 이 샌드박스 내부의 코드가 왕국에 접근할 수 있는 유일한 통로는 엄격하게 제한되어야 한다.

왕국이 샌드박스와 대화하는 법

문제는 이 두 세계가 영원히 분리되어 있을 수만은 없다는 것이다. 샌드박스 안의 사용자가 왕국의 기능을 사용하고 싶을 때가 있다. 예를 들어, VM 내부에서 활동하여 얻은 자신의 ERC-20 토큰을, 왕국이 관리하는 '진짜' 네이티브 코인으로 바꾸고 싶을 수 있다. 혹은 VM 내부의 스마트 컨트랙트가 왕국의 은행 모듈에 접근해 네이티브 코인을 전송하고 싶을 수도 있다.

이때 필요한 것이 바로 게이트웨이, 즉 '프리컴파일(Precompile)'이라 불리는 다리다. 프리컴파일은 VM의 특정 주소에 하드코딩된, 왕국으로 통하는 일종의 공식 창구다. 샌드박스 안의 코드가 이 특정 주소로 메시지를 보내면, VM은 이 메시지를 일반적인 코드로 처리하지 않고, 왕국이 미리 정해둔 '신뢰받는' 네이티브 코드에 직접 전달한다.

이 게이트웨이는 매우 엄격하게 통제된다. 예를 들어, VM 내부의 공격자가 이 게이트웨이를 통해 "왕국의 금고에 있는 모든 USDC를 내 주소로 보내라"고 명령할 수는 없다. 게이트웨이는 오직 정해진 기능만을 수행하며, 이 모든 명령은 반드시 호출자의 신원을 기반으로 한다. 즉, 샌드박스 안의 A가 은행 게이트웨이를 통해 "10 코인을 B에게 보내라"고 요청하면, 게이트웨이는 "A의 네이티브 계좌"에서 돈이 빠져나가도록 처리한다. A가 B의 계좌를 건드릴 수는 없다. 이 프리컴파일은 샌드박스 안의 존재들에게 왕국의 기능을 제한적으로 사용할 수 있게 해주면서도, 왕국의 보안을 유지하는 핵심적인 다리 역할을 한다.

'토큰 변환 모듈'의 딜레마

이 구조는 대부분의 경우 안전하게 작동한다. 샌드박스가 왕국을 호출(VM -> NF)하는 것은 언제나 호출자의 권한으로 제한되기 때문이다. 진짜 딜레마는 그 반대 방향의 호출이 필요할 때 발생한다. 즉, '신뢰받는 왕국'이 '신뢰할 수 없는 샌드박스' 내부의 코드를 호출해야만 하는 경우다.

이것이 바로 '토큰 변환 모듈'이 마주한 딜레마다. 이 모듈의 '변환 함수'는 사용자의 ERC-20 토큰(VM 내부의 자산)을 받고, 그것을 소각했는지 확인한 뒤, 그 대가로 네이티브 코인(왕국의 자산)을 발행(Mint)해주는 기능이다. 이 과정을 안전하게 처리하려면, 왕국(네이티브 모듈)은 사용자가 ERC-20 토큰을 정말로 소각했는지 '확인'해야만 한다.

이 '확인'을 위해, 왕국의 '신뢰받는' 네이티브 코드는 'VM 호출기'를 통해 샌드박스 안에 있는 사용자의 '신뢰할 수 없는' ERC-20 컨트랙트의 전송 함수를 '호출'(Interact)해야 한다.

어떤 경우는 이 호출 순서가 치명적이다. 일부 코드는 The DAO 해킹을 유발했던, 보안의 제1원칙을 위배하는 'Check-Interact-Effect' 패턴을 따르기도 한다.

  1. Check (확인): 네이티브 코드가 사용자의 ERC-20 잔액을 확인한다.
  2. Interact (상호작용): 네이티브 코드가 '신뢰할 수 없는' VM 컨트랙트의 전송 함수를 호출한다.
  3. Effect (효과): 만약 2번 호출이 오류 없이 반환되면, 네이티브 코드는 네이티브 코인을 발행(Mint)한다.

이 순서에서 2번 'Interact'가 실행되는 순간, 악성 코드는 네이티브 코드가 3번 'Effect' 단계(네이티브 코인 발행)에 도달하기 전에 1번 'Check'를 다시 호출하여 잔액이 남아있다고 속이고 '이중 발행'을 유도할 수 있다. 왕국은 샌드박스를 신뢰할 수 없지만, 토큰 변환을 위해서는 샌드박스를 호출해야만 하는 피할 수 없는 딜레마에 빠진 것이다.

'실행자(msg.sender)' 논쟁: 권한은 누구의 것인가?

이러한 딜레마 속에서, 우리는 가장 치명적인 시나리오, 즉 '금고 탈취(Honeypot)' 공격 시나리오를 상상해볼 수 있다. 만약 이 재진입 공격이 가능하다면, 공격자는 단순히 '이중 발행'에 그치지 않을 것이기 때문이다.

개략적인 시나리오는 이렇다.

  1. '토큰 변환 모듈'(왕국)이 악성 컨트랙트에게 토큰 전달을 요청한다(Interact).
  2. 이때, VM 내부에서 이 악성 컨트랙트의 실행자(msg.sender)가 모종의 이유로 '공격자'가 아니라, 이 호출을 시작한 '토큰 변환 모듈' 자신의 주소로 설정된다면? (권한 상승)
  3. 이를 노리고 악성 컨트랙트는 즉시 '은행 게이트웨이'(프리컴파일)를 재진입 호출한다.
  4. 은행 게이트웨이에 가진 코인을 모두 내놓으라는 명령을 내린다.
    • from: msg.sender (상승된 권한)
    • to: 공격자의 주소
    • amount: 모듈의 금고(트레저리)에 락업된 모든 '진짜' 네이티브 자산
  5. 은행 게이트웨이는 이 명령이 '토큰 변환 모듈' 자신에게서 온 합법적인 요청이라고 판단하고, 금고의 모든 자산을 공격자에게 전송한다.

이 시나리오는 상상할 수 있는 최악의 재앙이다. 하지만 다행히도, 잘 설계된 시스템은 실수 없이 이 치명적인 공격을 방어하는 로직을 갖추고 있다. 결정적인 방어 로직으로, VM을 호출하는 함수를 사용할 때, EVM 내부에서 그 컨트랙트의 msg.sender가 '모듈 자신'의 주소가 아닌, 트랜잭션을 처음 시작한 '공격자'의 주소로 설정되도록 명시적으로 설계하면 된다.

이것은 간단하지만 엄청난 차이를 만든다. EVM 내부에서 악성 컨트랙트가 날뛰더라도, 그 컨트랙트의 msg.sender는 여전히 '공격자' 자신이다. 공격자가 은행 게이트웨이를 악의적으로 호출하더라도, 은행 게이트웨이는 "공격자(from)가 공격자(to)에게 보낸다"고 인식할 뿐이다. 공격자는 모듈의 권한을 탈취(Impersonation)할 수 없으며, 모듈의 금고에 있는 '진짜' 자산에는 절대 손댈 수 없다.

이 '신뢰 경계'를 설계하는 것이 가장 어려운 이유

그러나 앞으로도 이러한 '신뢰 경계'를 설계하는 것이 어려운 이유는 많다. 하나의 방어선이 성공했다고 해서, 모든 공격이 막히는 것은 아니기 때문이다.

  1. CEI 패턴 위반 잔존: msg.sender 방어에도 불구하고, '토큰 변환 모듈'은 여전히 'Check-Interact-Effect' 순서를 따르고 있다. 이는 공격자가 '이중 발행'을 시도할 수 있는 논리적 틈을 남긴다. 이 문제를 해결하려면, 모듈은 외부 호출 이후에 잔액이 실제로 줄었는지 '사후 검증(Post-Check)'하는 방어 로직을 추가해야 한다.
  2. 경제적 취약점(Griefing)의 존재: 이 논리적 결함과는 완전히 무관하게, 많은 시스템은 '비동기 콜백 핸들러'라는 또 다른 다리에서 '가스 그리핑'이라는 치명적인 경제적 공격을 발견한다. 이 공격은 왕국에 비용을 전가하여 시스템의 생존성(Liveness) 자체를 마비시킨다. 이것은 재진입이나 권한 탈취와 같은 기술적 문제가 아니라, '비용을 지불하는 주체'를 잘못 설계한 경제적 인센티브의 결함이다.
  3. 다층적 방어의 필요성: 왕국이 샌드박스와 대화할 때마다, 이 시스템은 논리적 공격(재진입), 경제적 공격(그리핑), 권한 탈취(msg.sender), 그리고 시간적 오류(비동기)라는 네 가지 위협에 동시에 노출된다. 그리고 이게 전부일까? 앞으로도 더 많은 요소들이 발견될 것이고 그것들을 모두 조심해야 할 것이다.

신뢰 경계의 설계가 어려운 이유는 이처럼 다층적인 방어가 필요하기 때문이다. 하나의 문을 잠갔다고 해서 안전한 것이 아니다. 공격자는 두 번째, 세 번째 문을 두드릴 것이다. 네이티브 왕국이 샌드박스와 대화하기 위해 만든 '게이트웨이'는, 그 자체로 시스템의 가장 복잡하고 위험한 심장부다. 이 경계를 설계하는 것은 기술의 문제가 아니라, 신뢰와 권한, 그리고 순서에 대한 철학의 문제다. 그리고 이 철학에 작은 균열이라도 생긴다면, 시스템 전체가 그 대가를 치르게 된다.

3줄 요약

  1. '네이티브 프레임워크'(왕국)와 '가상 머신'(샌드박스)은 서로 다른 신뢰 수준을 가지며, 이 둘을 연결하는 '게이트웨이'(프리컴파일)는 보안의 핵심이다.
  2. '토큰 변환 모듈'은 왕국이 샌드박스를 호출해야 하는 딜레마를 가지며, 이 과정에서 재진입 공격의 위험에 노출된다.
  3. 대부분의 시스템은 '실행자(msg.sender)' 권한을 올바르게 전달하므로 공격을 효과적으로 방어하지만, 여전히 'Check-Interact-Effect' 패턴을 위반하므로 '이중 발행' 등의 결함을 종합적으로 검토하여야 한다.