토큰 승인: 당신이 계속 부여하고 있는 권한들

·8분 읽기·작성자: SSP Editorial Team
스마트 컨트랙트에 건네지는 열쇠로 표현된 토큰 승인 권한 더미의 일러스트레이션

토큰 승인: 당신이 계속 부여하고 있는 권한들

DeFi 앱과 상호작용할 때마다 — DEX에서 스왑, 대출 시장 예치, ERC-20 브리지 — 거의 확실히 토큰 승인을 한 차례 부여한 셈이다. 대부분의 사용자는 이런 트랜잭션을 몇 초 안에 서명하고는 그 존재를 잊는다. 그러나 그 한 번의 approve 트랜잭션은 Ethereum에서 당신이 하는 일 중 가장 결과가 큰 것 중 하나다: 그것은 smart contract에 당신의 토큰을 움직일 영구적 권한을 넘긴다, 대개 추가 확인 없이. 이 가이드는 토큰 승인이 무엇인지, 왜 dApp이 그것을 요구하는지, "무제한 allowance" 패턴의 위험, 그리고 SSP를 통해 승인에 서명할 때 이 모든 것이 무엇을 의미하는지를 풀어낸다.

승인이 왜 존재하는가

ERC-20 토큰은 ETH처럼 당신의 지갑 "안"에 저장되지 않는다. ERC-20 토큰 컨트랙트는 장부다: 주소에서 잔액으로의 매핑이다. 당신이 USDC 1,000개를 보유하고 있을 때, 체인 위에 실제로 존재하는 것은 USDC 컨트랙트 안에서 당신의 주소가 1,000 단위를 소유한다고 적힌 한 줄이다.

잔액이 토큰 컨트랙트 안에 살고 있기 때문에, 오직 그 컨트랙트만이 그것을 움직일 수 있다. 친구에게 USDC를 보낼 때, 당신의 지갑은 그 토큰의 transfer 함수를 직접 호출한다.

하지만 DEX의 경우, 다른 컨트랙트(router)가 스왑을 실행하기 위해 당신의 주소에서 USDC를 끌어와야 한다. router는 토큰 컨트랙트 안으로 손을 뻗어 당신의 잔액을 가져갈 수 없다. ERC-20은 이 문제를 두 단계 위임 패턴으로 해결한다:

  1. 당신이 토큰 컨트랙트에서 approve(spender, amount)를 호출한다. 이는 allowance를 설정한다: spender가 당신 토큰의 amount까지 움직일 권한이 있다는 기록이다.
  2. spender가 나중에 토큰 컨트랙트에서 transferFrom(yourAddress, destination, amount)를 호출한다. 컨트랙트는 allowance를 확인하고, 그만큼 차감한 뒤 토큰을 움직인다.

승인은 열쇠다. 그것이 없다면 router는 당신의 USDC를 전혀 건드릴 수 없다.

실제로 무엇이 부여되고 있는가

approve(spender, amount)를 글자 그대로 읽어보자. 당신은 ERC-20 컨트랙트에 이렇게 말하고 있다: "이 주소는 내가 바꾸기 전까지, 언제든, 몇 번의 트랜잭션에서든 내 토큰을 최대 이만큼까지 인출할 수 있다."

여기서 몇 가지가 따라 나온다:

  • 일회성 행위가 아닌 영구 허가. 한 번 부여되면 allowance가 소진되거나 취소될 때까지 spender는 토큰을 끌어오는 데 당신의 서명을 다시 필요로 하지 않는다.
  • 토큰별로 따로따로. DEX router를 USDC에 대해 승인했다고 DAI를 만질 수 있는 것은 아니다; DAI는 별도 승인이 필요하다.
  • spender별로 따로따로. 같은 dApp의 다른 컨트랙트라도 자신만의 allowance를 가진다.
  • 만료 없음. ERC-20에는 내장 마감일이 없다. 2023년에 설정한 allowance는 소진되거나 취소되지 않는 한 2026년에도 여전히 활성이다.

etherscan 같은 블록 익스플로러에서 토큰 컨트랙트의 allowance(owner, spender) 뷰 함수를 읽어 어떤 allowance든 확인할 수 있다.

무한 allowance 패턴

승인이 금액 단위라면, 왜 거의 모든 DEX는 지금 당장 스왑할 금액만이 아니라 — 보통 2^256 - 1, "unlimited" 또는 MaxUint256으로 표시되는 — 엄청나게 큰 숫자를 요구할까?

답은 UX와 가스다. DEX가 스왑마다 거래 크기와 같은 새 allowance를 요구한다면, 매번 승인 트랜잭션을 지불해야 하고 하나의 행위처럼 느껴지는 일에 트랜잭션 두 건을 확인해야 할 것이다. 한 번에 무제한 allowance를 요청해 두면 이후로는 거래당 트랜잭션 한 건으로 자유롭게 스왑할 수 있게 된다.

진심으로 편리하다. 그리고 진심으로 더 위험하다. 무제한 allowance는 spender 컨트랙트가 해당 토큰의 현재 잔액 전부 — 그리고 미래의 모든 잔액 — 를 언제든 당신이 다른 무엇도 서명하지 않은 채로 비울 수 있도록 허락받은 상태라는 뜻이다.

잘 알려져 있고 감사를 받았고 변경 불가능한 컨트랙트라면 실질적 위험은 보통 낮다. 막 배포된 dApp, 업그레이드 가능한 프록시, 들어본 적 없는 컨트랙트라면 무제한 allowance는 당신이 하려던 거래보다 훨씬 넓은 표면적이다.

진짜 위험: 당신의 토큰으로 통하는 상시 열쇠

승인의 위험은 승인 트랜잭션 자체에 있지 않다. 그 이후에 일어나는 일에 있다. 몇 가지 시나리오를 떠올려보자:

  • spender 컨트랙트에 버그가 있다. 공격자가 그 transferFrom 로직을 악용하고, 0이 아닌 allowance를 가진 모든 지갑이 비워진다.
  • spender 컨트랙트가 업그레이드 가능하다. 업그레이드 키를 쥔 자가, 활성 allowance를 가진 모든 사람의 토큰을 끌어가는 새 로직을 배포한다.
  • 당신은 악성 클론을 승인했다. 피싱 사이트가 실제 dApp의 URL을 흉내냈고, 승인한 컨트랙트는 처음부터 공격자 통제하에 있었다.
  • spender의 서명 키가 탈취되었다. 공식 컨트랙트 자체는 멀쩡하지만 운영자의 지갑이 그렇지 않아, allowance가 침입자에 의해 행사되고 있다.

어느 경우든 비워지는 그 순간 당신은 아무것도 서명하지 않는다. 공격자가 필요로 하는 유일한 권한은 당신이 몇 주 또는 몇 달 전에 부여한 그 승인이다. "필요한 것만 승인하고, 더 이상 쓰지 않는 것은 취소하라"는 편집증이 아니다. 일을 시켜본 모든 시공업자에게 집 여분 열쇠를 그대로 맡기지 않는 것과 같은 원리다.

승인은 어떻게 조용히 쌓이는가

DeFi에서 1~2년 활동한 지갑은 이미 수십 건의 승인을 쌓아두고 있다: 사용해 본 모든 DEX, 새 컨트랙트를 거친 모든 aggregator, 모든 대출 시장 예치, 모든 NFT 마켓플레이스, 모든 브리지. 사용자는 거의 취소하러 돌아오지 않는다. 그 결과는 영구 권한의 긴 꼬리다 — 다수는 무제한이고, 다수는 사용자가 더 이상 거래하지 않는 컨트랙트에 부여되어 있다.

이것이 침묵의 공격 표면이다. 어떤 단일 트랜잭션에도 드러나지 않는다. 평범한 사용의 누적된 잔여물이다. 그 목록을 감사하고 정리하는 일은 자기수탁에서 가장 효과적인 보안 습관 중 하나다 — 다음 글은 이를 SSP에서 토큰 승인 취소하기에서 구체적으로 다룬다.

더 새로운 패턴: EIP-2612 permit

ERC-20은 현대 DeFi 대부분보다 앞서 등장했고, approve-그리고-swap이라는 두 트랜잭션의 춤은 오래전부터 어색한 것으로 인식되어 왔다. EIP-2612는 이를 지원하는 토큰을 위해 대안을 도입했다: 체인에 approve 트랜잭션을 보내는 대신, 사용자는 특정 spender, 금액, 데드라인을 승인하는 오프체인 메시지(permit)에 서명한다. dApp은 그 서명을 스왑과 함께 한 건의 트랜잭션으로 제출한다.

permit은 가스 효율적이고, 범위가 한정되어 있으며(명시적 금액과 만료가 포함된다), 데드라인이 만료를 강제하기 때문에 매달려 남아 있을 가능성이 더 적다. 모든 ERC-20이 지원하지는 않는다 — USDC와 DAI는 지원하고, 더 오래된 토큰들 다수는 그렇지 않다 — 그러나 지원되는 곳에서는 일반적으로 장기 approve allowance보다 더 안전하다. 다만 permit 서명 자체도 피싱의 대상이 될 수 있다: "로그인"을 요청하는 악성 사이트가 그 아래에 permit을 슬쩍 끼워 넣을 수 있다. 무엇에 서명하는지 잘 읽자.

SSP 안에서 이것이 의미하는 바

SSP는 자기수탁형 2-of-2 멀티시그 지갑이다: 모든 온체인 트랜잭션은 SSP 브라우저 확장과 SSP Key 모바일 앱에 의해 공동 서명된다. Ethereum 및 SSP가 지원하는 EVM 네트워크(Polygon, Base, BNB Smart Chain, Avalanche)에서는 이것이 Schnorr 집계 서명을 가진 ERC-4337 smart account로 구현되어 있다 — 하지만 애플리케이션 레이어에서 보면 승인은 다른 어떤 트랜잭션과도 다르지 않게 보인다.

기억해 둘 만한 몇 가지:

  • 승인은 당신의 2-of-2가 공동 서명해야 하는 트랜잭션이다. dApp이 approve를 요청할 때 SSP는 다른 tx처럼 그것을 노출한다. 확인 전에 두 기기 모두에서 spender 주소와 요청 금액을 볼 수 있다.
  • 한 번 부여되면 spender는 다시 SSP를 필요로 하지 않는다. 멀티시그는 승인의 그 순간을 보호하지, 그 이후의 영구 권한을 보호하지 않는다. 악성 컨트랙트에 무제한 allowance를 부여하면, 멀티시그는 이후에 일어날 비워짐으로부터 당신을 구해주지 않는다.
  • spender 주소를 주시하라. dApp들은 때때로 router를 업그레이드한다; 승인 화면의 spender가 기대한 컨트랙트와 다르면 멈추고 확인해야 한다.
  • WalletConnect로 시작된 승인도 똑같아 보인다. dApp이 페이지 내에서 요청하든 WalletConnect를 통해 요청하든, 흐름과 위험은 동일하다.

들여놓을 만한 습관들

몇 가지 구체적인 실천이 승인 표면을 다룰 만하게 유지해 준다:

  • 가능한 가장 작은 allowance를 선호하라. dApp이 "정확한 금액"과 "unlimited" 중 선택을 제시할 때, 정기적으로 쓰지 않는 컨트랙트에 대해서는 정확한 금액이 더 안전한 기본값이다.
  • 무제한 승인을 약속으로 다뤄라. 신뢰하고 자주 쓰는 소수의 컨트랙트에만 남겨두라. 나머지는 모두 범위를 좁히라.
  • 주기적으로 감사하라. 분기에 한 번씩, 각 체인에서 활성인 승인을 목록화하고 더 이상 쓰지 않는 모든 것을 취소하라. revoke.cash 같은 도구가 이를 일상으로 만들어 준다.
  • 낯선 dApp을 경계하라. 감사 이력이 없는 새 프로토콜이 무제한 allowance를 요구한다면, 이는 DeFi에서 가장 위험한 조합이다.
  • 승인을 부여하는 키를 보호하라. SSP의 멀티시그가 진입 장벽을 크게 높이지만, 기본 위생은 여전히 적용된다 — 시드 문구 모범 사례를 참고하라.

가져갈 만한 정신적 모델

토큰 승인은 클릭이 아니다. 그것은 열쇠다. 각각은 당신이 빼낼 때까지 자물쇠 안에 남아 있고, 각각은 그것의 소지자에게 당신이 아직 벌지도 않은 토큰을 움직일 능력을 부여한다. 신중히 쓰면 allowance는 DeFi가 돌아가게 만드는 배관이다. 일회용 클릭처럼 다루면, 그것들은 당신이 떠안은 줄도 잊은 채 천천히 쌓여가는 위험이 된다.

부여하는 권한을 이해하고, dApp이 허락하는 곳에서는 범위가 좁은 허가를 선호하며, 상시 승인을 일정한 주기로 가지치기하라. 더 깊은 프로토콜 세부는 ethereum.org의 ERC-20 표준 문서가 표준 참고서다. Ethereum에서 SSP를 처음 쓰는 중이라면, SSP의 Ethereum 가이드가 기본을 다룬다.

이 글 공유하기

관련 글