교육 목적으로만 제공되며 투자 조언이 아닙니다. 재진입 결함은 컨트랙트 자산에 빠르고 되돌릴 수 없는 손실을 일으킬 수 있으며, 단일 방어책이나 감사만으로 안전을 입증할 수 없습니다.
핵심 답변
컨트랙트 로직이 외부 호출을 하고, 원래 실행이 끝나 불변 조건이 복구되기 전에 피호출자가 콜백하면 재진입 공격이 발생할 수 있습니다. 오래된 잔액, 지분, 권한 또는 가격이 보이면 콜백은 일관되지 않은 상태를 이용해 동작을 반복하거나 다른 동작에 영향을 줄 수 있습니다.
콜백은 같은 함수에 들어가거나 네이티브 통화를 전송할 필요가 없습니다. 토큰 훅, NFT 수신자 콜백, 볼트 또는 전략 호출, 플래시론 콜백은 신뢰할 수 없는 코드에 제어권을 넘길 수 있습니다. 재진입은 함수, 컨트랙트, 모듈을 가로지를 수 있고, 읽기 전용 재진입은 다른 프로토콜이 일시적인 값을 사용하게 할 수도 있습니다.
작동 방식
일반적인 악용 순서는 다음과 같습니다.
- 공격자가 상태 변경 함수에 들어가 초기 검사를 통과합니다.
- 취약한 컨트랙트가 회계를 끝내기 전에 주소, 토큰 또는 프로토콜을 호출합니다.
- 공격자가 제어하는 코드가 이전 상태가 보이는 동안 원래 컨트랙트나 연결된 컨트랙트에 진입합니다.
- 중첩 호출이 효과를 반복하거나 공유 상태를 변경하고, 불변 조건이 깨진 채 실행이 되돌아옵니다.
외부로 제어권을 넘기는 동작은 저수준 호출처럼 명시적일 수도 있고 토큰 전송, 수신자 훅, 안전 민팅, 전략 어댑터 또는 임의 호출 인터페이스 뒤에 숨을 수도 있습니다. revert는 보통 해당 호출 트리를 되돌리지만, 공격자는 가치를 빼내거나 회계를 훼손한 뒤 정상 반환하는 성공적인 중첩 순서를 구성할 수 있습니다.
방어책은 계층적으로 적용해야 합니다.
- 검사-효과-상호작용을 적용해 먼저 검증하고 관련 내부 효과를 확정한 뒤 마지막에 외부와 상호작용합니다.
- 명백한 호출이 있는 함수뿐 아니라 보호할 불변 조건을 공유하는 모든 진입점에 재진입 가드를 적용합니다.
- 적절한 경우 수취인이 청구하는 방식을 쓰고 신뢰할 수 없는 토큰, 수신자, 훅, 프록시, 통합 호출을 줄입니다.
- 컨트랙트 간 불변 조건을 정의하고 악성 콜백, 함수 간 경로, 중첩 멀티콜, 읽기 전용 소비자를 테스트합니다.
- 설계가 지원한다면 구현 검토에 모니터링, 일시 중지, 사고 대응 통제를 결합합니다.
예시
값을 보낸 다음에만 사용자 잔액을 지우는 볼트를 가정해 보겠습니다.
function withdraw() external {
uint256 amount = balances[msg.sender];
require(amount > 0, "empty balance");
(bool ok, ) = msg.sender.call{value: amount}("");
require(ok, "transfer failed");
balances[msg.sender] = 0;
}수신자는 balances[msg.sender]에 이전 금액이 남아 있는 동안 제어권을 얻습니다. 수신 함수가 withdraw()를 다시 호출해 같은 검사를 통과하고 추가 전송을 요청할 수 있습니다. 호출 전에 잔액을 갱신하면 이 특정 창이 닫히고, 가드는 중첩 진입을 거부할 수 있습니다. 그러나 다른 진입점이나 연결된 컨트랙트가 같은 미완성 불변 조건을 노출할 수 있으므로 어느 변경도 전체 시스템의 안전을 입증하지 않습니다.
위험
- 가드가 한 함수만 보호하고 다른 함수가 같은 상태를 노출합니다.
- 로컬 호출 순서는 올바르지만 컨트랙트 간 불변 조건이 콜백 중 일관되지 않습니다.
- 토큰, 수신자, 플래시론 콜백 또는 전략 어댑터가 예상치 못하게 외부 코드를 실행합니다.
- view 함수가 일시적 가격이나 환율을 게시하고 다른 프로토콜이 같은 트랜잭션에서 이를 사용합니다.
- 업그레이드, 모듈 또는 스토리지 레이아웃 변경이 기존 잠금을 우회하거나 손상합니다.
- 테스트가 재귀 출금만 다루고 함수 간, 컨트랙트 간, 읽기 전용 경로를 빠뜨립니다.
사용자에게 감사 배지나 재진입 가드는 통제가 있다는 증거이지 보장이 아닙니다. 업그레이드, 통합, 특권적 긴급 조치는 공격 표면을 바꿀 수 있습니다. 승인을 제한하고 노출을 줄이며 배포된 구현을 확인하고 손실을 되돌릴 수 있다고 가정하지 마십시오.
흔한 오해
- 재진입은 한 출금 함수를 반복하는 것뿐입니다. 다른 함수나 컨트랙트에 들어가거나 읽는 쪽에 일관되지 않은 데이터만 노출할 수도 있습니다.
- 네이티브 통화 전송만 콜백을 일으킵니다. 토큰 표준, 수신자 훅, 프로토콜 통합도 외부 코드를 실행할 수 있습니다.
- 가드나 검사-효과-상호작용이 컨트랙트를 안전하게 만듭니다. 적용 범위, 모든 공유 진입점, 컨트랙트 간 불변 조건을 계속 검토해야 합니다.
- 감사를 통과하면 재진입이 배제됩니다. 감사 범위는 제한되며 이후 변경과 검토되지 않은 통합이 새 경로를 만들 수 있습니다.
관련 주제
출처
- 보안 고려 사항 - Solidity Documentation (확인일: 2026-08-21)
- 스마트 컨트랙트 보안 - ethereum.org (확인일: 2026-08-21)
- ReentrancyGuard - OpenZeppelin Documentation (확인일: 2026-08-21)
- SC08:2026 재진입 공격 - OWASP Smart Contract Security (확인일: 2026-08-21)