솔리디티에서 'reentrancy' 공격이란 무엇인가요?
_____A1: reentrancy 공격은 스마트 컨트랙트가 외부 콜을 호출할 때, 호출된 외부 컨트랙트가 다시 원래 컨트랙트의 함수를 재진입하여 상태가 예상과 다르게 변경되도록 악용하는 공격 기법입니다. 주로 이더 전송 시 함수 실행 순서가 꼬여 발생합니다.
Q2: reentrancy 공격이 왜 위험한가요?
A2: 공격자가 재진입을 통해 컨트랙트의 상태가 업데이트되기 전에 악의적으로 여러 번 호출 가능하여, 중복 출금 또는 자산 도난과 같은 심각한 보안 문제를 일으킬 수 있습니다.
Q3: reentrancy 공격이 발생하는 일반적인 상황은?
A3: 주로 Withdrawal 패턴에서, 사용자의 출금 요청 시 이더를 전송하는 call 또는 send 함수 전에 사용자의 잔액을 먼저 차감하지 않거나, 이더 전송 후 상태 갱신을 할 때 발생합니다.
Q4: reentrancy 공격을 방지하는 방법은 무엇인가요?
A4:
1. Checks-Effects-Interactions 패턴 : 상태 변경(효과 적용) 후 외부 호출 수행
2. Reentrancy Guard 사용 : OpenZeppelin의 `ReentrancyGuard` 컨트랙트를 사용해 재진입 방지
3. 단일 호출 외부 전송 지양 : `transfer` 또는 `send` 대신 `call`을 사용하고 호출 처리 신중히
4. Pull over Push 방식 사용 : 출금 요청을 기록만 해두고 별도 함수에서 출금하도록 설계
Q5: reentrancy 공격이 유명했던 사례는?
A5: 2016년 이더리움 DAO 공격이 대표적 사례로, 이 공격으로 약 6천만 달러 상당의 이더가 도난당했으며, 이 문제로 이더리움 하드포크가 진행되었습니다.
Q6: reentrancy 공격과 관련된 솔리디티 코드 예시는?
A6:
```solidity
mapping(address => uint) balances;
function withdraw() public {
uint amount = balances[msg.sender];
require(amount > 0);
require(success);
balances[msg.sender] = 0; // 상태 변경이 너무 늦게 이루어져 공격 가능
}
```
위 코드에서 외부 호출인 `call` 이후에 상태를 변경하기 때문에 재진입 시 중복 출금 가능.
Q7: 개선된 방어 코드 예시는?
A7:
```solidity
function withdraw() public {
uint amount = balances[msg.sender];
require(amount > 0);
balances[msg.sender] = 0; // 상태 먼저 변경
(bool success, ) = msg.sender.call{value: amount}("");
require(success);
}
```
또는 `ReentrancyGuard`를 사용하여 재진입을 방지할 수 있습니다.
---
요약하면, 솔리디티의 reentrancy 공격은 외부 호출 시 스마트 컨트랙트가 악성 컨트랙트에 의해 반복 호출되어 자산 도난을 초래하는 공격이며, 상태 변경과 외부 호출 순서 관리 및 재진입 방지 장치 도입으로 예방 가능합니다.
이 공격은 주로 이더리움과 같은 블록체인 플랫폼에서 작성된 솔리디티(Solidity) 스마트 계약에서 발생할 수 있습니다.
Reentrancy 공격의 원리 Reentrancy 공격은 주로 다음과 같은 방식으로 이루어집니다: 1. 상태 변경 : 공격자는 스마트 계약의 특정 함수를 호출하여 자산을 인출하거나 상태를 변경합니다.
2. 콜백 함수 : 이 함수가 외부 계약을 호출하거나 이더를 전송할 때, 공격자는 해당 외부 계약의 콜백 함수를 통해 다시 원래의 계약으로 돌아와 같은 함수를 재호출할 수 있습니다.
3. 상태 불일치 : 원래 계약의 상태가 업데이트되기 전에 공격자가 재진입하여 추가적인 자산을 인출하거나 상태를 변경할 수 있습니다.
이로 인해 계약의 상태가 예상과 다르게 변하게 됩니다.
예시 가장 유명한 Reentrancy 공격의 예로는 2016년의 DAO 공격이 있습니다.
이 공격에서는 공격자가 DAO 계약의 `withdraw` 함수를 호출하여 이더를 인출하는 과정에서, 이더를 전송하는 동시에 다시 `withdraw` 함수를 호출하여 반복적으로 자산을 인출했습니다.
이로 인해 DAO는 수천만 달러에 해당하는 이더를 잃게 되었습니다.
Reentrancy 공격 방지 방법 Reentrancy 공격을 방지하기 위해 개발자들은 여러 가지 방법을 사용할 수 있습니다: 1. Checks-Effects-Interactions 패턴 : 이 패턴은 함수의 상태를 변경한 후 외부 계약과 상호작용하는 방식입니다.
즉, 먼저 상태를 업데이트하고, 그 다음에 외부 호출을 수행하여 재진입 공격의 가능성을 줄입니다.
```solidity function withdraw(uint amount) public { require(balances[msg.sender] >= amount); // 상태 변경 balances[msg.sender] -= amount; // 외부 호출 payable(msg.sender).transfer(amount); } ```
2. Mutex (상호 배제) : 함수가 실행되는 동안 다른 호출이 이루어지지 않도록 잠금을 설정하는 방법입니다.
이 방법은 코드가 복잡해질 수 있으며, 잘못 구현될 경우 데드락(교착 상태)을 초래할 수 있습니다.
3. Reentrancy Guard : 특정 플래그를 사용하여 함수가 재진입되는 것을 방지하는 방법입니다.
이 플래그는 함수가 실행 중일 때 true로 설정되고, 함수가 종료되면 false로 설정됩니다.
```solidity bool internal locked; modifier noReentrancy() { require(!locked, "No reentrancy allowed"); locked = true; _; locked = false; } function withdraw(uint amount) public noReentrancy { // withdraw logic } ```
4. Gas Limit : 외부 호출 시 가스 한도를 설정하여 공격자가 재진입할 수 있는 기회를 줄이는 방법입니다.
그러나 이 방법은 완벽한 해결책이 아닙니다.
결론 Reentrancy 공격은 스마트 계약의 보안에 있어 매우 중요한 문제입니다.
개발자들은 이러한 공격을 방지하기 위해 다양한 패턴과 기법을 사용해야 하며, 코드 리뷰와 테스트를 통해 취약점을 사전에 발견하고 수정하는 것이 필수적입니다.
블록체인 기술이 발전함에 따라 이러한 보안 문제에 대한 인식과 대응이 더욱 중요해지고 있습니다.
작성자:
박지혜 [비회원]
| 작성일자: 1년 전
2024-11-22 19:32:14
조회수: 122 | 댓글: 0 | 좋아요: 0 | 싫어요: 0
조회수: 122 | 댓글: 0 | 좋아요: 0 | 싫어요: 0
내용이 부정확하다면 싫어요를 클릭해주세요.