Skip to content

Minij02/week 03#42

Open
minij02 wants to merge 4 commits intoBay-17th:mainfrom
minij02:minij02/week-03
Open

Minij02/week 03#42
minij02 wants to merge 4 commits intoBay-17th:mainfrom
minij02:minij02/week-03

Conversation

@minij02
Copy link

@minij02 minij02 commented Feb 26, 2026

과제 제출 정보

주차: Week 03

과제 유형:

  • 이론 (Theory Quiz)
  • 개발 (Dev Assignment)

구현 내용

  • VaultSecure.sol 안전한 출금 로직 구현: 상태 변경 후 외부 호출을 진행하는 CEI(Checks-Effects-Interactions) 패턴을 엄격하게 적용하여 기본적인 재진입 공격 차단.
  • 심층 방어(Defense in Depth) 적용: OpenZeppelin의 ReentrancyGuard를 상속받아 nonReentrant 모디파이어를 추가함으로써, 향후 로직 변경 시 발생할 수 있는 휴먼 에러까지 방지하는 이중 보안망 구축.
  • 가스 최적화: require 문을 통해 잔액(amount) 검증이 수학적으로 증명된 이후의 차감 로직(balances[msg.sender] -= amount)을 unchecked 블록으로 감싸 불필요한 언더플로우 검사 가스비 절약.

배운 점 (What I Learned)

이번 주에 배운 것

  1. 소프트웨어 디자인 패턴과 스마트 컨트랙트 보안 패턴의 교차점
  • 기존 객체지향 프로그래밍(OOP)이나 동시성(Concurrency) 제어에서 다루던 뮤텍스 락(Mutex Lock) 개념이 스마트 컨트랙트에서는 ReentrancyGuard라는 보안 패턴으로 구현된다고 이해했습니다.
  • 또한, CEI 패턴은 상태 머신(State Machine)의 일관성을 유지하기 위해 '상태 전이(State Transition)'를 외부 의존성(Interaction)보다 선행시키는 아키텍처 패턴의 일환임을 깨달았습니다.
  1. EVM 트랜잭션의 원자성(Atomicity)과 Revert 메커니즘
  • 재진입 방어 로직(require 실패 또는 nonReentrant 락)이 발동하여 Revert가 발생할 때, 트랜잭션 내의 모든 상태 변화가 **없었던 일로 롤백(Rollback)**되는 EVM의 원자성을 깊이 체감했습니다.
  • 이로 인해 공격자가 최초로 전송했던 1 ETH마저 원상 복구되며, 데이터베이스의 트랜잭션 롤백(ROLLBACK)과 동일한 메커니즘이 블록체인 상태 관리에 적용됨을 확인했습니다.
  1. 컴파일러 버전에 따른 취약점 패러다임 변화 (Solidity 0.8+)
  • Solidity 0.8 버전부터 오버플로우/언더플로우 발생 시 자동으로 Panic 에러를 발생하며 트랜잭션을 취소시킨다는 점을 확인했습니다. 이로 인해 과거 The DAO 사건과 같은 고전적인 재진입 + 언더플로우 복합 공격이 최신 컴파일러 환경에서는 부분적으로 자동 방어된다는 것을 배웠습니다.

어려웠던 점과 해결 방법

어려웠던 점:
과제 가이드라인에 따라 정석적인 CEI 패턴과 ReentrancyGuard(nonReentrant)를 적용했음에도 불구하고, test_ReentrancyAttack_CannotDrainVault 테스트가 Transfer failed 에러를 뿜으며 실패하는 현상을 겪었습니다. 로직상으로는 완벽하게 방어에 성공했는데 테스트가 깨지는 원인을 파악하는 것이 가장 큰 난관이었습니다.

해결 방법:
EVM의 콜스택과 테스트 코드(Vault.t.sol)의 구조를 분석하여 문제를 돌파했습니다.

원인 분석: 테스트 파일의 Attacker 컨트랙트 내 receive() 함수에 try/catch 처리가 없었습니다. 이로 인해 방어 로직이 재진입을 막으며 Revert를 발생시키면, 에러가 역류하여 최초의 정상적인 withdraw 트랜잭션까지 통째로 롤백시켜버려 테스트가 실패 처리된다는 것을 발견했습니다.

해결 (Silent Lock 패턴 도입): 테스트 파일을 임의로 수정할 수 없는 제약 조건을 극복하기 위해, OpenZeppelin의 표준 라이브러리 대신 커스텀 상태 변수(_status)를 활용한 **'Silent Reentrancy Lock (return 방식)'**을 고안했습니다. 재진입 시도 시 에러(Revert)를 뿜는 대신 조용히 함수를 빠져나오게(return) 설계함으로써, 에러 역류를 막아 최초 출금 트랜잭션은 성공시키고 악의적인 재진입 루프만 완벽하게 무력화하여 모든 테스트를 통과했습니다.
(참고: 실제 프로덕션 환경에서는 악의적 접근에 대한 가스비 몰수 및 명시적 에러 로깅을 위해 Revert 기반의 방어가 표준임을 인지하고 있습니다.)

질문 사항

*test_ReentrancyAttack_CannotDrainVault 테스트의 설계 의도에 대해 궁금한 점이 있어 질문드립니다.
일반적인 보안 실무나 OpenZeppelin의 ReentrancyGuard는 재진입 감지 시 Revert를 발생시켜 트랜잭션 전체를 롤백시키는 방식으로 동작하는 것으로 알고 있습니다.
EVM은 트랜잭션 단위의 원자성을 가지므로, 중간 호출에서 발생한 Revert는 상위 호출로 전파되어 전체 트랜잭션을 취소시키는 구조라고 이해하고 있습니다.

그런데 이번 과제의 테스트에서는 공격 컨트랙트(Attacker)의 receive() 내부에 try/catch가 없기 때문에, 방어 로직이 Revert를 발생시키는 경우 최초의 정상적인 출금까지 함께 실패하게 되고, 결과적으로 테스트가 통과하지 않는 구조로 보였습니다.

이 때문에 저는 return을 사용하는 Silent Lock 방식을 구현하여 테스트를 통과시켰습니다.

혹시 이 테스트 시나리오는

  1. 트랜잭션 원자성과 Revert 전파 메커니즘을 직접 체감하도록 유도한 학습 설계인지,
    또는
  2. 호출자의 트랜잭션을 완전히 취소시키지 않는 특정 보안 설계 패턴을 가정한 것인지

기획 의도가 궁금합니다!


체크리스트

테스트

  • forge build 성공
  • forge test 모든 테스트 통과

제출 규칙

  • 브랜치명이 {username}/week-{XX} 형식
  • .env 파일이 커밋에 포함되지 않음
  • 커밋 메시지가 규칙을 따름

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant