재진입 공격은 Ethereum 및 EVM 호환 체인에서 스마트 계약 보안에 가장 악명 높고 지속적인 위협 중 하나로 남아 있습니다. 다년간 업계의 노력에도 불구하고, 새로운 프로토콜들은 여전히 재진입 취약점에 직면하고 있으며, 이는 때로 수백만 달러에 달하는 막대한 재정적 손실로 이어지고 있습니다. 이러한 공격은 외부 호출과 토큰 전송이 빈번한 DeFi 대출, 스테이킹, 브릿지 계약에서 자주 발생합니다.
이 글에서는 재진입 공격의 메커니즘을 분석하고, 빈번한 취약 계약 패턴을 식별하며, 검증된 완화 전략을 공유합니다. Soken에서 255건 이상의 스마트 계약 감사를 기반으로, 세밀한 Solidity 코딩 관행과 포괄적인 테스트 워크플로가 어떻게 재진입 위협을 차단할 수 있는지 강조합니다. 또한 delegatecall 오용과 플래시론 전략과 같은 관련 리스크도 연결하여, 모든 진지한 Web3 개발자 또는 보안 감사자가 알아야 할 폭넓은 이해를 제공합니다.
재진입 공격이란 무엇이며 스마트 계약을 어떻게 악용하는가?
재진입 공격은 악의적인 계약이 취약한 계약의 함수가 최초 실행을 완료하기 전에 반복적으로 호출하여, 계약의 불일치한 상태를 악용하는 경우를 말합니다.
실제로 재진입 취약점은 계약 내부에서 상태 변경과 외부 호출이 잘못 순서화되어 발생합니다. 계약이 ETH 또는 토큰을 call 또는 transfer를 통해 신뢰하지 않는 주소로 전송할 때, 해당 수신자는 상태 변수가 업데이트되기 전에 재귀적으로 취약 함수에 재진입할 수 있습니다. 이는 공격자가 자금을 고갈시키거나 계약 로직을 예기치 않게 조작할 수 있게 합니다.
예를 들어, 2016년 악명 높은 DAO 해킹은 재진입 결함을 통해 약 6천만 달러가 탈취된 사건으로, 매우 중요한 교훈이 되었습니다. 이 사건 이후 업계는 근본 원인이 외부 호출 후에만 계약 잔액이나 상태가 업데이트되어 공격자가 악용할 수 있는 불일치 상태의 창이 생긴다는 점을 확인했습니다.
Soken 감사 전문가 인사이트: “최근 DeFi 보안 감사의 40% 이상에서 재진입 패턴이 어떤 형태로든 존재하며, 종종 프록시 계약이나 다중 호출을 포함한 미묘한 변형을 보입니다. 이를 식별하려면 단일 계약 검사를 넘어선 철저한 계약 간 호출 분석이 필요합니다.”
재진입 취약점을 일으키는 흔한 Solidity 패턴
재진입 취약점은 일반적으로 계약이 내부 상태 변수를 업데이트하기 전에 외부 호출(ETH 전송 또는 토큰 호출)이 발생하는 특정 작업 순서에서 비롯됩니다.
가장 취약한 패턴은 다음과 같습니다:
function withdraw(uint256 amount) public {
require(balances[msg.sender] >= amount);
(bool success, ) = msg.sender.call{value: amount}("");
require(success);
balances[msg.sender] -= amount;
}
여기서는 call.value()를 이용한 외부 호출이 사용자 잔액을 차감하기 전에 발생합니다. 외부 호출 중 공격자의 fallback 함수가 재귀적으로 withdraw()를 호출하여 잔액 업데이트 전에 자금을 고갈시킬 수 있습니다.
주요 위험 요소는 다음과 같습니다:
- 상태 업데이트 전에 외부 호출: 잔액을 업데이트하기 전에 ETH 또는 토큰 전송.
- 주의 없이
call또는send사용: 저수준 함수의 외부 호출이 체크를 우회하며 fallback 함수를 실행할 수 있음. - 재진입 가드 부재: 뮤텍스나 기타 잠금 메커니즘이 없음.
- delegatecall을 포함한 복잡한 호출 체인: 호출된 계약 내 외부 호출을 통해 간접적으로 재진입이 발생할 수 있음.
반면, 안전한 패턴은 호출 순서를 반대로 합니다:
function withdraw(uint256 amount) public {
require(balances[msg.sender] >= amount);
balances[msg.sender] -= amount; // 먼저 상태 업데이트
(bool success, ) = msg.sender.call{value: amount}("");
require(success);
}
Solidity에서 재진입 공격을 방지하는 방법: 최선의 관행과 도구
재진입에 대한 가장 효과적인 방어책은 외부 호출 전에 상태를 업데이트하고 명시적인 재진입 방지 패턴을 결합하는 것입니다.
표준 완화 기법:
-
Checks-Effects-Interactions 패턴:
외부 호출을 수행하기 전에 내부 상태를 항상 업데이트합니다. 이는 외부 호출 중에 불일치한 계약 상태를 최소화합니다. -
재진입 가드 / 뮤텍스:
OpenZeppelin의ReentrancyGuard는 간단한 뮤텍스로 중첩 호출을 차단합니다. 아래와 같이 적용할 수 있습니다:
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
contract SecureContract is ReentrancyGuard {
mapping(address => uint256) balances;
function withdraw(uint256 amount) public nonReentrant {
require(balances[msg.sender] >= amount);
balances[msg.sender] -= amount;
(bool success, ) = msg.sender.call{value: amount}("");
require(success);
}
}
-
외부 호출 제한:
중요한 함수 내에서 전송 또는 상호작용을 최소화하세요 — 사용자가 명시적으로 자금을 인출하는 pull 방식을 권장합니다. -
정적 분석 및 자동화 도구:
Slither 및 Soken 자체 감사 도구가 재진입을 탐지합니다. -
최신 Solidity 버전 및 기능 사용:
Solidity 0.8+는 내장된 오버플로우 검사를 제공하지만, 재진입 위험은 여전히 존재하므로 코드 안전성과 디자인 패턴을 함께 적용해야 합니다.
Soken 메서드: 감사 시 수동 위협 모델링, 심볼릭 실행, 퍼즈 테스트를 통합하여, 일반 스캐너가 놓치기 쉬운 복잡한 재진입 흐름도 탐지합니다.
실제 재진입 악용 사례 및 피해 규모
재진입 공격은 인지가 높아졌음에도 여전히 빈번합니다. 아래는 대표적인 해킹 사례를 비교한 표로, 다양한 유형과 비용을 보여줍니다:
| 사건명 | 날짜 | 피해 액수 | 악용 패턴 | 주요 교훈 |
|---|---|---|---|---|
| The DAO Hack | 2016-06 | 약 6천만 달러 | splitDAO 제안에서 재진입 | 외부 호출 전에 상태 업데이트 필요 |
| The Lendf.Me Hack | 2020-04 | 2,500만 달러 이상 | 플래시론으로 증폭된 재진입 | 재진입 가드 + 플래시론 대응 결합 필요 |
| Euler Finance Hack | 2023-03 | 1억 9,700만 달러 | 중첩된 재진입 + delegatecall | 복잡한 호출 체인이 위험 증가시킴 |
| 최근 DeFi 브릿지 해킹 | 2025-11 | 1,500만 달러 | 토큰 전송 재진입 악용 | 교차 계약 호출 집중 감사 필요 |
참고: Soken의 감사는 특히 교차 계약 호출과 fallback 함수가 동적으로 트리거될 수 있는 토큰 표준 주위를 포함한 다층 보안을 강조합니다.
Delegatecall과 플래시론 벡터: 재진입 위험 증폭
Delegatecall은 호출 계약의 컨텍스트에서 코드를 실행하므로, 재진입 위험을 모호하게 하고 공격 면적을 확대할 수 있습니다. 특히 플래시론과 결합될 때 더욱 위험합니다.
재진입 공격은 종종 플래시론을 활용해 재귀적 자금 고갈에 필요한 자본을 극대화합니다:
- 플래시론은 단일 트랜잭션 내에 대규모 즉시 유동성을 제공합니다.
- 공격자는 플래시론 자금을 사용해 재진입 호출을 트리거하여 프로토콜 유동성을 고갈시키고, 이후 대출금을 상환합니다.
- Delegatecall 사용은 외부 신뢰할 수 없는 코드가 계약 상태를 변경하거나 재진입을 허용할 수 있습니다.
보안 인사이트: “Soken 경험에 따르면, 재진입 완화는 단일 계약을 넘어서 프로토콜 전반의 설계에 적용되어야 하며 — 특히 delegatecall과 플래시론이 공존할 때 그러합니다. 계약은 재진입 가드와 입장 검증, 플래시론 전용 검사(대출 한도, 재귀 호출 제한)를 반드시 결합해야 합니다.”
비교 표: 흔한 재진입 방지 기법
| 기법 | 설명 | 장점 | 단점 | 사용 상황 |
|---|---|---|---|---|
| Checks-Effects-Interactions | 외부 호출 전에 상태 업데이트 | 간단하고 효과적 | 준수가 필요 | 기본 보안 관행 |
| 재진입 가드(뮤텍스) | 솔리디티 수정자를 사용하여 재진입 차단 | 재귀 호출을 명확하게 차단 | 약간의 가스 오버헤드 발생 | 모든 인출 함수 권장 |
| Pull Payment 패턴 | 사용자가 자발적으로 자금을 인출하게함 | 자동 전송 위험 최소화 | 사용자 상호작용 필요 | 에스크로 및 스테이킹 계약에 이상적 |
| 정적/동적 분석 | 자동 및 수동 코드 감사 | 취약점 조기 탐지 | 복잡한 교차 계약 흐름 놓칠 수 있음 | 배포 전 필수 보증 |
| 플래시론 & Delegatecall 검사 | 호출자 및 트랜잭션 컨텍스트 검증 | 론 증폭 재진입 방지 | 구현 복잡 | 플래시론 존재하는 DeFi 프로토콜에 중요 |
Solidity 코드 예제: 재진입 취약 계약 및 그 수정법
아래는 최소한의 취약 계약과 재진입 가드 적용 강화 버전입니다:
// 취약: 잔액 업데이트 전에 인출
contract Vulnerable {
mapping(address => uint256) public balances;
function deposit() external payable {
balances[msg.sender] += msg.value;
}
function withdraw(uint256 amount) external {
require(balances[msg.sender] >= amount, "Insufficient funds");
(bool success, ) = msg.sender.call{value: amount}("");
require(success, "Transfer failed");
balances[msg.sender] -= amount; // 취약: 외부 호출 후 상태 업데이트
}
}
OpenZeppelin의 ReentrancyGuard와 효과-상호작용 순서를 사용해 수정:
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
contract Secure is ReentrancyGuard {
mapping(address => uint256) public balances;
function deposit() external payable {
balances[msg.sender] += msg.value;
}
function withdraw(uint256 amount) external nonReentrant {
require(balances[msg.sender] >= amount, "Insufficient funds");
balances[msg.sender] -= amount; // 외부 호출 전에 상태 업데이트
(bool success, ) = msg.sender.call{value: amount}("");
require(success, "Transfer failed");
}
}
전문 팁: 항상 checks-effects-interactions 패턴과 재진입 가드를 결합하세요. 계약 호출 그래프를 감사하고 퍼즈 테스트 도구로 다단계 재진입 취약점을 사전에 발견할 수 있습니다.
재진입 취약점은 특히 플래시론과 delegatecall을 사용하는 복잡한 DeFi 생태계에서 여전히 치명적인 위협입니다. 가장 효과적인 방어는 코드 규율(Checks-Effects-Interactions), 명확한 재진입 가드, 철저한 감사, 상황 인지 트랜잭션 검증을 결합하는 것입니다.
Soken은 255건 이상의 스마트 계약 감사를 통해 수동 및 자동화 기법을 통합해 재진입 및 관련 위험을 일관되게 식별하고 해결해오고 있습니다. 사용자 자금을 다루는 모든 프로젝트는 최선의 관행과 철저한 테스트 전략을 활용해야 합니다.
전문가 보안 컨설팅이 필요하신가요? Soken 팀은 255건 이상의 스마트 계약을 검토하고 20억 달러 이상의 프로토콜 가치를 보호했습니다. 포괄적 감사, 무료 보안 X-Ray 평가, 또는 암호화폐 규제 대응 지원이 필요하시면 언제든 도와드리겠습니다.