← Back to Blog

Access Control 실수로 $953M이 사라진 이유

2026-03-25 access-control smart-contract-security solidity defi-security vulnerability

Access Control 실수로 $953M이 사라진 이유

2025년 기준 스마트 컨트랙트 보안 사고 손실 카테고리 1위는 Access Control 취약점이다. 누적 피해액 $953M 이상. 복잡한 수학적 exploit이 아니라, 단순히 "누가 이 함수를 호출할 수 있는가"를 제대로 검증하지 않아 발생한다.

Access Control 취약점이란?

관리자 전용 함수(mint, pause, upgrade, transfer ownership 등)에 적절한 접근 제어가 없어 아무나 호출할 수 있는 상태.

취약한 코드

contract VulnerableToken {
    address public owner;

    // ⚠️ onlyOwner 체크 없음 — 누구나 민팅 가능
    function mint(address to, uint256 amount) external {
        _mint(to, amount);
    }

    // ⚠️ 초기화 함수가 보호되지 않음
    function initialize(address _owner) external {
        owner = _owner;
    }
}

사례 1: Poly Network (2021, ~$611M)

크로스체인 릴레이의 keeper 변경 함수가 충분히 보호되지 않아 공격자가 자신을 keeper로 등록. 모든 체인의 자금을 인출.

근본 원인: verifyHeaderAndExecuteTx()의 크로스체인 메시지 검증이 임의 컨트랙트 호출을 허용.

사례 2: Ronin Bridge (2022, ~$625M)

Axie Infinity의 Ronin Bridge에서 9개 validator 중 5개의 private key가 탈취됨. 다중 서명 threshold를 넘어 브릿지 자금 인출.

근본 원인: validator 수가 적고, 일부 키가 동일 인프라에 저장. 소셜 엔지니어링 공격에 취약.

사례 3: Nomad Bridge (2022, ~$190M)

업그레이드 과정에서 trusted root가 0x00으로 초기화됨. 모든 메시지가 자동으로 유효한 것으로 처리되어, 누구나 브릿지 자금 인출 가능.

근본 원인: initialize() 호출 시 잘못된 파라미터로 Merkle root가 zero로 설정.

방어 1: Ownable 패턴

import "@openzeppelin/contracts/access/Ownable.sol";

contract SafeToken is Ownable {
    function mint(address to, uint256 amount) external onlyOwner {
        _mint(to, amount);
    }
}

방어 2: Role-Based Access Control (RBAC)

import "@openzeppelin/contracts/access/AccessControl.sol";

contract SafeToken is AccessControl {
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");

    function mint(address to, uint256 amount) external onlyRole(MINTER_ROLE) {
        _mint(to, amount);
    }
}

방어 3: Initializer 보호

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";

contract SafeProxy is Initializable {
    function initialize(address _owner) external initializer {
        // initializer modifier가 재호출 방지
        _transferOwnership(_owner);
    }
}

방어 4: Timelock

관리자 함수 실행 전 대기 시간을 두어, 커뮤니티가 악의적 변경을 감지하고 대응할 시간을 확보.

import "@openzeppelin/contracts/governance/TimelockController.sol";

체크리스트

ContractScan으로 탐지하기

Slither의 unprotected-upgrade, Semgrep의 missing-access-control 룰 등으로 Access Control 누락을 자동 탐지합니다.
ContractScan 무료 스캔