궁금한 주제를 찾아보세요

비트코인, 스테이블코인, 온체인 데이터처럼 주제로 검색하세요.

다시 읽을 이야기

저장한 글은 이 브라우저에만 보관됩니다.

해설블록체인 기술

재진입 공격은 외부 호출과 상태 변경 순서를 어떻게 악용하나

외부 호출이 제어권을 넘긴 동안 같은 잔액을 다시 읽는 단일·교차 함수 재진입과 CEI·mutex·pull payment 방어를 사례로 설명합니다.

난이도 중급주제 카테고리와 개념 밀도 기준
출금 중인 금고로 호출 화살표가 다시 돌아와 상태 갱신 전 반복 인출을 만드는 재진입 개념도
주제의 이해를 돕기 위한 개념 일러스트
먼저 읽는 핵심

모든 외부 호출은 악성 callback으로 제어권을 넘길 수 있습니다.
Effects를 interaction보다 먼저 기록해 재호출이 최신 상태를 보게 합니다.
Guard 범위 밖 교차 함수와 외부 의존 상태도 함께 모델링해야 합니다.

재진입(reentrancy)은 계약 A가 토큰 전송이나 ETH CALL로 외부 계약 B에 제어권을 넘긴 뒤, A가 중요한 상태를 갱신하기 전에 B가 A의 함수로 다시 들어와 같은 권리나 잔액을 한 번 더 쓰는 공격입니다. 방어의 핵심은 입력과 권한을 검사하고, 잔액 차감이나 청구 완료 같은 상태 변경을 먼저 기록한 다음, 마지막에 외부 호출을 하는 ‘검사-반영-호출(checks-effects-interactions)’ 순서입니다. ReentrancyGuard의 잠금 장치는 같은 보호 영역으로 다시 들어오는 호출을 막아 줍니다. 그러나 보호하지 않은 함수, 다른 계약을 거친 콜백, 읽기 전용 관찰, 잘못된 상태 설계까지 자동으로 고쳐 주지는 않습니다.

취약점은 외부 호출 전의 낡은 상태입니다

금고 계약이 balances[user]를 읽고 user.call{value:amount}를 실행한 뒤에 잔액을 0으로 만든다고 해 보겠습니다. 사용자가 계약이라면 receive 함수 안에서 Vault.withdraw를 다시 부를 수 있습니다. 첫 번째 호출이 아직 잔액을 0으로 쓰지 않았기 때문에 두 번째 호출도 같은 금액으로 조건을 통과해 지급이 반복됩니다.

ETH 전송만 위험한 것은 아닙니다. ERC-777 훅, NFT safeTransfer 콜백, 대출 프로토콜의 임의 토큰·오라클 호출도 제어권을 넘깁니다. 잘 알려진 계약을 호출해도 그 계약이 다시 알 수 없는 계약을 부를 수 있습니다. 외부 호출인지 아닌지는 함수 이름이 아니라 실제 CALL 계열 실행 추적으로 판단합니다.

재진입의 본질은 두 번 호출됐다는 사실보다, 두 번째 호출이 첫 번째 호출이 끝나기 전의 상태를 유효하다고 읽었다는 데 있습니다.

JOBCOIN 해설

그림으로 보기보상 발표와 지급을 구분하기
보상 공지, 청구 조건, 실제 지급 확인이 서로 다른 단계임을 보여주는 개념도
그림과 함께 짚어볼 본문 내용

그림은 이 주제의 공통 개념을 단순화한 설명입니다. 아래 항목에서 이 글의 구체적인 조건과 예외를 함께 읽어보세요.

  1. 취약점은 외부 호출 전의 낡은 상태입니다

    금고 계약이 balances[user]를 읽고 user.call{value:amount}를 실행한 뒤에 잔액을 0으로 만든다고 해 보겠습니다.

  2. Checks-Effects-Interactions로 순서를 뒤집습니다

    안전한 출금 함수는 먼저 금액과 권한을 검사하고 balances[user]=0을 저장한 다음 외부 전송을 시도합니다.

  3. Mutex는 범위를 정확히 설정해야 합니다

    OpenZeppelin ReentrancyGuard는 진입 상태를 표시해 두었다가 nonReentrant 함수가 끝나면 되돌립니다.

설명을 위한 개념도 · 실제 가격·거래 내역·통계가 아닙니다.

Checks-Effects-Interactions로 순서를 뒤집습니다

안전한 출금 함수는 먼저 금액과 권한을 검사하고 balances[user]=0을 저장한 다음 외부 전송을 시도합니다. 콜백이 다시 들어와도 이미 0인 상태를 보게 되므로 지급 조건을 통과하지 못합니다. 외부 전송이 실패하면 require로 거래 전체를 되돌려, 먼저 기록한 상태 변경도 원래대로 돌릴 수 있습니다.

이 순서는 단순히 코드 줄의 순서가 아니라, 외부 호출 전에 불변 조건이 성립하는지를 보는 방법입니다. 총부채, 지분 발행량, 논스, 청구 표시처럼 함께 움직이는 값이 모두 갱신돼 있어야 합니다. 변수 하나만 먼저 바꾸고 다른 함수가 아직 갱신되지 않은 변수를 읽는다면 함수를 넘나드는 재진입 위험이 남습니다.

재진입 유형과 방어 초점
유형 재진입 경로 검토 항목
동일 함수 withdraw→콜백→withdraw 잔액 먼저 차감
교차 함수 withdraw→콜백→transfer 함께 쓰는 불변 조건
교차 계약 A→B→C→A 전체 호출 관계도
읽기 전용 콜백 도중의 조회 함수 호출 중간 가격·비율 노출
콜백 토큰 전송 훅에서 재호출 지원 토큰의 동작 방식

Mutex는 범위를 정확히 설정해야 합니다

OpenZeppelin ReentrancyGuard는 진입 상태를 표시해 두었다가 nonReentrant 함수가 끝나면 되돌립니다. 같은 잠금을 쓰는 보호 함수끼리는 서로 중첩 호출할 수 없으므로, 외부 진입 함수가 private 작업 함수를 부르는 구조가 흔합니다. 업그레이드형 환경이라면 선택한 구현의 초기 상태와 저장소 슬롯도 확인합니다.

출금 함수만 보호하고 같은 잔액 기록을 고치는 borrow·transfer를 열어 두면 콜백이 다른 진입점으로 들어올 수 있습니다. 여러 계약이 각자 잠금을 갖고 있어도 서로의 잠금은 공유하지 않습니다. 잠금이 있다는 사실보다, 어떤 불변 조건을 어느 진입점들이 함께 쓰는지를 표로 정리합니다.

  • 외부 CALL·토큰 훅·콜백 지점을 모두 표시합니다.
  • 지점마다 직전에 끝나지 않은 상태 변경을 찾습니다.
  • 같은 불변 조건을 쓰는 모든 공개 진입점을 묶습니다.
  • 호출 순서 규칙과 잠금을 각각 따로 시험합니다.
  • 악성 수신 계약으로 중첩 콜백을 재현합니다.

Pull payment와 실패 처리를 함께 설계합니다

거래 하나에서 여러 사용자에게 ETH를 밀어 보내면, 수신자 한 명의 실패가 정산 전체를 막거나 콜백이 들어올 틈을 넓힙니다. 사용자별 지급액을 먼저 기록하고 각자 따로 출금하게 하는 ‘인출형 지급(pull payment)’ 방식은 외부 호출을 작은 함수 하나로 모읍니다. 그래도 출금 함수 자체에는 호출 순서 규칙과 실패 반환값 검사가 필요합니다.

저수준 호출이 false를 돌려줬는데 무시하면 잔액만 깎이고 지급은 실패할 수 있습니다. 반대로 실패했을 때 지급액을 다시 더해 주는 보상 로직이 콜백과 얽히면 지급액이 중복될 수 있습니다. 성공 조건과 되돌림 경계를 상태 기계로 보고 시험합니다.

Gas 제한을 방어선으로 의존하지 않습니다

transfer의 2,300 가스 지급량이 콜백을 영원히 막아 준다는 가정은 옵코드 가스 비용 변경과 수신 계약의 동작 때문에 튼튼하지 않습니다. Solidity 문서도 Ether 전송에는 코드 실행이 포함되며, call은 더 많은 가스를 넘겨 재진입 가능성을 연다고 설명합니다. 가스 제한 대신 상태 갱신 순서와 명시적인 잠금을 씁니다.

시험에는 동일 함수, 교차 함수, 여러 프로토콜을 거치는 콜백, 실패와 성공을 번갈아 내는 수신 계약을 넣습니다. 실행 추적에서 호출 깊이만 세지 말고, 각 호출 단계가 읽고 쓴 슬롯과 외부 자산 이동을 맞춰 봐야 실제로 불변 조건이 깨졌는지 확인할 수 있습니다.

자주 묻는 질문

nonReentrant modifier 하나면 모든 재진입이 막히나요?

그 잠금을 함께 쓰는 함수로의 진입만 막습니다. 보호 밖의 함수, 다른 계약의 상태, 읽기 전용 재진입은 따로 검토해야 합니다.

View 함수도 재진입 위험이 있나요?

상태를 쓰지 않더라도, 거래 도중의 불완전한 가격·비율을 외부 프로토콜이 읽어 판단에 쓰면 읽기 전용 재진입 문제가 생길 수 있습니다.

ETH를 transfer로 보내면 안전한가요?

가스 지급량을 영구적인 방어선으로 믿으면 안 됩니다. 호출 순서 규칙, 인출형 지급, 반환값 검사를 적용해야 합니다.

더 깊이 읽기

본문에서 다룬 개념과 확인 절차를 다음 글에서 이어서 살펴보세요.

참고한 원문 자료

자료 확인 기준일 2026.09.27
  1. Solidity Security Considerations — Reentrancydocs.soliditylang.org
  2. OpenZeppelin ReentrancyGuard sourcegithub.com
작성 방식과 확인 범위
출처 수집
원문 링크 2개 제공
작성·확인
공식 기관 자료를 근거로 AI 도구를 활용해 작성했고, 운영자가 발행 전에 읽어 확인합니다.
전문가 검토
세무·법률 전문가 검토는 거치지 않았습니다.

투자·세무 판단은 전문가와 상의하세요. 제도와 거래소 안내는 바뀔 수 있으니 원문의 적용 대상과 최신 안내를 함께 확인해 주세요.

이해를 위한 정보 콘텐츠

이 글은 특정 자산의 매수·매도 또는 수익을 권유하지 않습니다. 자료의 발표 시점과 이후 변경 사항을 함께 확인해 주세요.

편집 원칙 보기 →이 기사 정정 제보 →