Solidity 무한 민팅 공격 방지 방법

작성자

무한 발행 공격이란 무엇이며 어떻게 작동하나요?

무한 발행 공격은 토큰 발행 경로가 누가 발행할 수 있는지, 얼마나 존재할 수 있는지 신뢰성 있게 증명하지 못하는 취약점을 이용합니다. 공개 또는 잘못 보호된 mint 함수는 공격자가 프로토콜의 나머지 부분이 합법적인 ERC-20 토큰으로 간주하는 균형을 만들어낼 수 있도록 허용합니다.

발행 함수는 호출자의 권한, 발행 조건, 공급량 계산이 프로토콜의 경제 설계와 일치할 때만 안전합니다. 어떤 호출자라도 _mint와 유사한 로직에 접근하거나, 특권이 있는 호출자가 공급 한도를 우회할 수 있다면, 토큰의 희소성은 오로지 가정에 불과하게 됩니다.

일반적인 무한 발행 공격은 다음 순서를 따릅니다:

  1. 공격자는 외부 호출이 가능하거나 우회 가능한 접근 체크를 식별합니다.
  2. 공격자는 제어하는 수신자와 금액으로 함수를 호출합니다.
  3. 계약은 공격자의 잔액과 종종 totalSupply를 증가시킵니다.
  4. 공격자는 발행된 토큰을 DEX 풀, 렌딩 시장, 브릿지 또는금고로 전송합니다.
  5. 공격자는 인위적인 토큰을 실물 자산으로 교환하거나 담보로 사용합니다.
  6. 유동성 제공자, 대출자, 토큰 홀더는 그에 따른 경제적 손실을 감수합니다.

무한 발행의 취약점은 특히 토큰 잔액이 이를 생성하는 방식을 독립적으로 검증하지 않는 다른 계약에서 수용될 때 매우 위험합니다. 예를 들어, 대출 프로토콜은 의도된 배출과 접근 제어 체크가 누락된 토큰을 구별하지 않습니다.

어떤 Solidity 패턴이 무한 발행 취약점을 야기하나요?

무한 발행 취약점은 대부분 권한 누락, 공급 한도 누락 또는 외관상 적절하지만 의도하지 않은 관리 경로를 통한 권한 문제에서 발생합니다. 균형을 증가시킬 수 있는 모든 함수, 직접적이거나 간접적인 모든 함수를 검토하세요.

가장 명백한 패턴은 호출자 제한이 없는 공개 발행 함수입니다.

function mint(address to, uint256 amount) external {
    balanceOf[to] += amount;
    totalSupply += amount;
}

이와 같은 위험은 개발자가 잘못된 신원을 검사할 때도 발생합니다. 예를 들어, tx.origin은 발행 권한을 정의하는 데 사용해서는 안 되며, 공격자가 특권 지갑을 유도하여 악의적 중개 계약을 호출하게 할 수 있기 때문입니다. 마찬가지로, 수신자가 특권자인지 여부를 검사하는 것만으로 호출자가 토큰을 발행할 허가를 받았다는 것을 증명하지 않습니다.

다른 일반적인 원인들은 다음과 같습니다:

  • 누구든 호출 가능한 setMinter 함수로 공격자가 자신에게 발행 권한을 부여 가능
  • 업그레이드 가능 프록시로, 업그레이드 권한자가 발행 제한을 교체할 수 있음
  • 임의 메시지를 발행 지시로 신뢰하는 브릿지 또는 체인 간 핸들러
  • 하나의 발행 함수에서 제한을 적용하지만, 보상 청구 또는 이전 함수에서는 적용하지 않는 경우
  • 명목상의 한도를 무력화하는 산술 또는 단위 변환 오류
  • 소각 후 재발행이 검증되지 않는 소각-발행 회계 시스템

totalSupply 변수만으로 공급량 제어를 할 수 없습니다. 계약은 totalSupply + amount가 최대 공급량을 초과하는 모든 발행을 거부해야 하며, 이 원칙은 관리 복구, 브릿지 발행, 보상, 이전 등 모든 발행 경로에 적용되어야 합니다.

취약한 코드와 수정된 무한 발행 Solidity 코드의 예시는 무엇인가요?

취약한 무한 발행 계약은 제한 없는 발행 함수를 노출합니다. 어떤 외부 계정이든 임의의 양을 스스로 발행할 수 있으며, 최대 공급량 제한이 피해를 방지하지 못합니다.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract VulnerableInfiniteMintToken {
    string public constant name = "Vulnerable Token";
    string public constant symbol = "VULN";

    uint256 public totalSupply;
    mapping(address => uint256) public balanceOf;

    event Transfer(address indexed from, address indexed to, uint256 amount);

    function mint(address to, uint256 amount) external {
        // 취약점: 누구든 임의의 토큰을 생성할 수 있음
        totalSupply += amount;
        balanceOf[to] += amount;
        emit Transfer(address(0), to, amount);
    }

    function transfer(address to, uint256 amount) external returns (bool) {
        require(balanceOf[msg.sender] >= amount, "잔액 부족");
        unchecked {
            balanceOf[msg.sender] -= amount;
        }
        balanceOf[to] += amount;
        emit Transfer(msg.sender, to, amount);
        return true;
    }
}

수정된 버전은 관리자를 별도로 지정하고 허가된 발행자만이 공급량 한도 내에서 발행할 수 있게 합니다. 한도는 권한 부여 대신 사용할 수 없으며, 권한은 한도와 별개로 다뤄져야 합니다. 두 가지 모두 다른 실패 모드를 해결합니다.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract CappedRoleMintToken {
    string public constant name = "Capped Token";
    string public constant symbol = "CAP";

    address public immutable admin;
    uint256 public immutable maxSupply;
    uint256 public totalSupply;

    mapping(address => bool) public isMinter;
    mapping(address => uint256) public balanceOf;

    event Transfer(address indexed from, address indexed to, uint256 amount);
    event MinterUpdated(address indexed minter, bool allowed);

    modifier onlyAdmin() {
        require(msg.sender == admin, "관리자 아님");
        _;
    }

    modifier onlyMinter() {
        require(isMinter[msg.sender], "발행자 아님");
        _;
    }

    constructor(uint256 maxSupply_) {
        require(maxSupply_ > 0, "제로 한도");
        admin = msg.sender;
        maxSupply = maxSupply_;
        isMinter[msg.sender] = true;
    }

    function setMinter(address minter, bool allowed) external onlyAdmin {
        isMinter[minter] = allowed;
        emit MinterUpdated(minter, allowed);
    }

    function mint(address to, uint256 amount) external onlyMinter {
        // 수정: 인가된 발행은 변경 불가능한 한도 내에서만 가능
        require(totalSupply + amount <= maxSupply, "한도 초과");
        totalSupply += amount;
        balanceOf[to] += amount;
        emit Transfer(address(0), to, amount);
    }

    function transfer(address to, uint256 amount) external returns (bool) {
        require(balanceOf[msg.sender] >= amount, "잔액 부족");
        unchecked {
            balanceOf[msg.sender] -= amount;
        }
        balanceOf[to] += amount;
        emit Transfer(msg.sender, to, amount);
        return true;
    }
}

프로토콜이 보상, 브릿지, 금고 소유권 또는 이전을 통해 발행한다면, 스마트 계약 감사 및 보안 검토를 통해 각 발행 경로를 권한 및 공급 불변성과 다시 추적할 수 있습니다.

Solidity에서 무한 발행 공격을 방지하려면 어떻게 해야 하나요?

무한 발행 공격을 방지하려면 명확한 권한 체계, 강제된 공급 제약, 제한된 관리 권한, 발행 경로가 권한에 의해 검증됐음을 입증하는 테스트가 필요합니다. 설계는 공급이 고정, 한도, 배출 기반, 담보 기반 또는 브릿지 기반인지에 따라 다릅니다.

기술 방지하는 것 가스 또는 복잡도 비용 한계
역할 제한된 mint 임의의 외부 호출자가 토큰 발행 역할 체크의 스토리지 읽기 손상된 발행자가 자신의 권한 내에서 계속 발행 가능
불변 최대 공급 설계된 토큰 공급을 초과하는 발행 한 번의 산술 검사 인가된 당사자가 사전에 전체 한도를 조기에 발행하는 것 방지 불가
별도 관리자와 발행자 역할 발행자가 모르고 더 많은 권한 부여 추가 역할 관리 코드 관리자는 여전히 중요한 신뢰 구간
다중 서명 또는 시간 제한 관리자 단일 키 변경에 따른 관리 변경 운영 지연 및 거버넌스 비용 이미 배포된 잘못된 발행 로직 수정 불가
중앙집중식 내부 _mint 경로 보조적 발행 함수에서 한도 체크 누락 리팩토링 필요 모든 외부 경로에 _mint 검증 필요
발행 활동 이벤트 모니터링 이상한 발행을 늦게 감지 오프체인 모니터링 인프라 이미 유효한 악의적 발행을 되돌릴 수 없음

고정 공급 토큰의 경우, 배포 시 전체 할당량을 발행하고 이후 발행을 모두 제거하는 것이 가장 강력한 패턴입니다. 배출 기반 토큰의 경우, 일정 정책을 정의하고, 내부 발행 함수에서 총 한도를 강제하며, 외부 함수마다 별도 권한 목적을 부여하는 방법이 적절합니다.

Soken 방법론은 발행을 자산 생성 경계로 취급하며, 편리한 기능이 아닙니다. 토큰 잔액을 늘리는 모든 경로는 권한 모델, 명확한 경제적 한계, 시험할 수 있는 불변 조건을 갖추어야 합니다.

어떤 실제 사건이 무한 발행 공격으로 분류되었나요?

기록된 무한 발행 사건들은 토큰 발행 결함이 기존 자산과 새로운 프로토콜에 영향을 미치는 경우를 보여줍니다. 아래 사건들은 DefiLlama 해킹 데이터베이스에서 Infinite Mint로 분류된 사례입니다.

프로토콜 날짜 손실액 체인
Elrond 2022-06-05 $113.0M Elrond
Paid Network 2021-03-05 $27.0M Ethereum
Gala 2024-05-20 $22.3M Ethereum
Haven Protocol 2021-07-03 $8.2M Haven
OcelotDex 2024-09-20 $4.9M Ethereum
TSR 2026-06-01 $2.5M BSC
Raydium AMM 2026-06-10 $1.3M Solana
Flooring Protocol 2026-06-08 $50만 Ethereum

이 사건들은 핵심 경제적 특성을 공유하는데, 즉시 뒷받침되지 않는 토큰이 통합된 시장에 진입하면 공격이 토큰 계약 그 이상으로 확장됩니다. 유동성 풀은 인위적 공급을 매수하고, 대출 시장은 이를 담보로 받아들일 수 있으며, 브릿지 또는 금고는 이 잔고를 더 많은 시스템으로 전파할 수 있습니다.

따라서 중요한 검증 질문은 “공격자가 mint를 호출할 수 있느냐?”가 아니라, “신뢰할 수 없거나 과도하게 신뢰하는 경로가 있으면 프로토콜이 뒷받침되지 않는 토큰을 유효 자산으로 인식하게 만들 수 있느냐?”입니다.

배포 전에 무한 발행 취약점을 어떻게 감지하나요?

무한 발행 취약점의 검출은 모든 잔액 증가 상태 전이를 맵핑한 후, 각 전이가 허가받았으며 의도한 공급량 불변성을 유지하는지 증명하는 작업입니다. 수작업 검토는 토큰 계약에서 시작해 브릿지, 금고, 보상 배포기, 업그레이드 모듈, 관리 도구 등을 계속 진행해야 합니다.

이 검토 체크리스트를 사용하세요:

  1. 잔액, 지분, 공급량 계산기, 내부 발행 함수에 대한 모든 쓰기를 찾기
  2. 각 외부 발행 경로가 목적에 적합한 호출자 권한을 갖는지 검증
  3. 모든 경로가 동일한 한도 또는 배출 제약을 강제하는지 확인
  4. 발행자 또는 관리자 역할의 추가, 제거, 교체 함수를 검토
  5. 발행 로직에 도달할 수 있는 업그레이드, 브릿지, 이전, 콜백 경로 추적
  6. 특권 계정이 예상치 못하게 한도, 발행자 또는 회계 단위를 변경할 수 있는지 체크
  7. 발행 이벤트와 totalSupply가 실제 잔액 증가와 일치하는지 검증
  8. 토큰을 담보, 유동성 또는 금고 자산으로 수락하는 통합 검증

Slither는 외부 호출 함수, 특권 상태 변경 및 의심스러운 접근 컨트롤 패턴을 열거하는 데 유용하지만, 프로토콜의 공급 모델이 경제적으로 적합하다는 것을 확립할 수는 없습니다. 특히 간접 발행 경로와 계약 간 권한은 수작업으로 판단하는 것이 필수적입니다.

Foundry의 퍼징은 임의 호출자가 발행하지 못하고 공급이 한도를 넘지 않도록 검증할 수 있습니다. 불변 조건 테스트는 특히 효과적입니다:

function invariant_supplyNeverExceedsCap() public view {
    require(token.totalSupply() <= token.maxSupply(), "공급 한도 초과");
}

두 번째 불변 조건으로는, 오직 승인된 행위자만 공급량을 늘릴 수 있다는 점을 검증합니다. Soken 검증 방법론에서는 이 특성이 mint라는 이름의 함수뿐만 아니라 모든 공개 함수에서 테스트됩니다. 접근 제어, 토큰 회계, 기타 계약 위험에 관한 추가 연구는 Soken Hub를 참고하세요.

보안 인사이트: Soken 방법론은 각 새 토큰 단위를 프로토콜이 만든 부채로 간주합니다. 계약이 그 부채의 승인을 누가 했는지, 왜 배포 모델 내에 남아 있는지 증명하지 못한다면, 그 발행 경로는 재설계가 필요한 보안 경계입니다.

역할 검사와 공급 한도는 무제한 발행을 유한, 책임 있는 운영으로 전환하지만, 모든 발행 경로에서 이를 반드시 사용해야 합니다. 중요한 엔지니어링 과제는 잔액 생성 기능을 중앙집중화하고, 우회가 불가능하도록 만드는 것입니다.

출시 또는 업그레이드 전에, 의도한 공급 불변 조건을 일반 언어로 작성하고, 이를 공유하는 발행 경로에 구현하며, Foundry 불변 조건 테스트로 강제하세요.

작성자

자주 묻는 질문

Solidity에서 무한 민팅 공격이란 무엇인가요?

무한 민팅 공격은 공격자가 승인 없이 토큰을 계속 생성하거나 최대 공급 한도 없거나 두 가지 조건이 결합된 경우 발생합니다. 이로 인해 공격자는 적법한 ERC-20 토큰으로 간주되는 임의 잔액을 얻을 수 있습니다.

무한 민팅 공격은 어떻게 작동되나요?

공개되거나 잘못 보호된 민팅 함수의 취약점을 이용해 공격자는 지속적으로 잔액을 생성할 수 있습니다. 이로 인해 프로토콜이 적법한 ERC-20 거래로 인식하는 토큰이 무한히 늘어납니다.

무한 민팅 공격이 어떤 피해를 초래하나요?

무한 민팅은 통제되지 않은 인플레이션으로 토큰의 거래 가치를 파괴할 수 있습니다. 새로 민팅된 토큰은 판매, 교환, 담보로 활용되며 유동성 풀을 고갈시키고, 대출이나 재무 시스템에 무담보 담보를 만들어 시장 충격을 유발할 수 있습니다.

Elrond 무한 민팅 사건이란 무엇인가요?

2022년 6월 5일, Elrond는 무한 민팅으로 인해 113백만 달러 손실을 기록했습니다. 이 사건은 공급 경계를 깨면 시장과 연동이 그것을 받아들여 체계적 손실로 이어질 수 있음을 보여줍니다.

Solidity 개발자는 어떻게 무한 민팅 공격을 방지할 수 있나요?

발행 권한과 공급 한도를 엄격히 증명하는 검증 절차를 마련하고, 모든 민팅 경로에 유효한 권한 부여를 적용하여 공격자가 임의의 토큰 잔액을 생성하지 못하게 해야 합니다.

채팅