스크립토 6기 하진원입니다.
스크립토 방학 스터디로 마스터링 이더리움 공부하고 있습니다.
컨트랙트와 상호작용하는 3자를 공격하는 패턴이다.
스마트 컨트랙트에 파라미터를 전달할 때 파라미터는 ABI 사양을 따른다.
이 때 예상되는 파라미터 길이보다 짧은 인코딩된 파라미터가 보낼수 있다.
이 경우 EVM은 인코딩된 파라미터 뒤에 0을 붙여 길이를 맞춘다.
만약 해당 컨트랙트와 상호작용하는 3자가 입력의 유효성을 검증하지 않는 경우 이는 문제가 된다.
만약 공격자가 주소가 0으로 끝나는 주소를 소유하고 있다면 쉽게 토큰을 훔칠 수 있다.
외부 어플의 입력 파라미터는 블록체인에 보내지기 전에 유효성 검증을 거쳐야 한다.
파라미터에 끝에 0이 붙는 것에서 문제가 발생하기 때문에 파라미터의 순서를 잘 정하면 방지할 수 있다.
외부 계정으로 이더를 보낼 때 주로 transfer를 사용하지만 send, call 연산코드 등을 사용할 수 있다.
send, call을 사용한 경우에 외부 호출이 실패한 경우에도 트랜잭션이 되돌아가지 않고 true, false를 반환한다.
보통 개발자는 외부 호출이 실패하면 원상태로 트랜잭션이 돌아가는 것을 기대하기 때문에 유의해야 한다.
send 대신 외부 트랜잭션이 실패할 경우 롤백되는 transfer 함수를 사용하여야 한다.
혹은 출금 패턴(withdrawal pattern)을 사용할 수 있다.
이 경우 각 사용자는 격리된 withdraw 함수를 호출해야 하고 이가 실패하면 그 결과를 직접 처리, 즉 사용자가 책임을 져야 한다.
사용자들이 서로 코드 실행을 경쟁하는 상태가 나오는 경우가 있다.
이더리움 노드는 트랜잭션을 풀링하여 블록으로 만든다.
채굴자는 문제를 풀어 블록을 생성하고 어떤 트랜잭션을 블록에 포함할 것인지 풀에서 선택한다.
공격자는 문제에 대한 해결책을 포함하는(?) 트랜잭션에 대한 정보를 트랜잭션 풀에서 찾고 그 문제를 푼 해결자의 권한을 변경, 취소하여 불리하게 바꿀 수 있다.
(정보를 어떻게 찾는다는 것인지 알 수 없음)
그 후 공격자는 그 트랜잭션에서 데이터를 얻어 gasPrice를 높여 트랜잭션을 생성하고 자신의 트랜잭션이 채굴되도록 할 수 있다.
예시로는 해시된 값의 프리이미지를 찾는 사용자가 답을 제출하면 금액을 받는 컨트랙트가 있다 가정을 할 수 있다. A가 해당 답을 파라미터로 solve를 호출하는 경우 공격자 B가 그 답을 트랜잭션 풀에서 볼 수 있다면 gasPrice만 올려 트랜잭션을 제출한다면 A보다 B의 트랜잭션이 먼저 올라가 제출될 것이고 금액 또한 B가 수령할 것이다.
DoS의 일반적인 정의는 대량의 접속을 유발해 해당 컴퓨터를 마비시키는 수법이다.
사용자가 일정 기간, 영구적으로 컨트랙트를 실행할 수 없게 만드는 공격이다.
외부에서 조작된 매핑, 배열을 통한 루핑
소유자가 distribute로 투자자들에게 토큰을 분배할때 나타난다.
공격자가 사용자 계정을 만히 만들어 for를 실행하는데 드는 가스가 블록 가스 한도를 넘기게 할 수 있다.
소유자 운영
소유자가 컨트랙트에서 특정 권한을 가지고 컨트랙트가 진행하기 위해 몇가지 작업을 수행해야 하는 경우가 있다.
이 경우 권한이 있는 사용자(소유자)가 개인키를 잃거나 사용할 수 없게 되면 전체 컨트랙트가 작동하지 않는, 하나의 주소가 컨트랙트를 결정하는 경우가 생긴다.
외부 호출을 기반으로 한 진행 상태
새로운 상태로 진행하기 위해서 어떤 주소로 이더를 보내거나 외부 소스의 입력을 기다려야 하는 컨트랙트의 경우 외부 호출이 실패하거나 외부적 요인으로 차단될 때 DoS 공격이 발생할 수 있다.
컨트랙트는 외부 사용자가 조작할 수 있는 데이터 구조에 대해 루프를 돌리면 안된다.
그 대신 각 투자자가 독자적으로 토큰을 청구할 수 있게 withdraw 함수를 호출하도록 해야한다.
컨트랙트의 상태를 변경하기 위해 권한 있는 사용자가 필요한 경우
권한 있는 사용자에 대한 안전장치를 사용할 수 있다.
첫번째는 소유자를 다중 컨트랙트로 만드는 방법이 있다.
두번째는 time-lock을 사용하는 것. 지정된 시간 이후에 사용자의 요청이 처리될 수 있는 매커니즘 추가!
블록 타임스탬프는 임의의 숫자를 위한 엔트로피, 시간 의존적 상태 변화 조건문 등에 사용된다.
그렇지만 채굴자는 타임스탬프를 약간 조정할 수 있기 때문에 타임스탬프를 사용하는 것은 위험성이 있다.
채굴자는 block.Timestamp와 그의 별칭인 now를 조작할 수 있다.
예를 들면 채굴자는 동기가 충분하다면 block.timestamp의 15의 나머지 값이 0이 되도록 timestamp를 선택할 수 있다.(단, 블록 타임스탬프는 단조롭게 증가하기 때문에 채굴자가 임의의 블록 타임스탬프를 선택할 수는 없고 머지 않은 미래로 블록을 설정하는 정도는 가능하다. 그렇기 때문에 약간 조정할 수 있다고 하는 것이다.)
블록 타임스탬프를 엔트로피를 생성하는 데 사용하면 안 된다.
시간을 사용하는 경우 block.number와 평균 블록 시간을 사용하여 시간을 추정한다.
생성자는 컨트랙트를 초기화할 때 중요하고 권한을 필요로 하는 작업을 수행하는 특수 함수이다.
과거 버전(v0.4.22 이전)에는 생성자가 포함된 컨트랙트와 이름이 같은 함수로 생성자가 정의되었는데 이 경우 컨트랙트 이름이 변경되었을때 생성자 이름도 같이 변경되지 않으면 생성자도 일반 함수로 취급받는 경우가 발생하였다.
생성자는 컨트랙트 이름과 일치하지 않는 경우 일반 함수로 작동한다.
v0.4.22에서 생성자를 별도로 지정하는 constructor 키워드가 도입되면서 해결되었다.
함수 내의 지역 변수는 그 타입에 따라 스토리지 또는 메모리를 기본으로 사용한다.
이 때 함수 내의 지역 변수를 초기화하지 않으면 문제에 노출되는데 이는 컨트랙트 내의 다른 변수값을 포함할 수 있기 때문이다.
솔리디티 컴파일러의 초기화되지 않은 스토리지 변수에 대한 경고를 무시하지 않는다.
솔리디티v0.4.24에서는 고정소수점, 부동소수점 숫자를 지원하지 않기 때문에 부동소수점 표현은 솔리디티에서 정수 유형(int타입)으로 구성해야 하며 이가 올바르게 구현되지 않으면 오류가 발생한다.
비, 비율을 사용할 때는 분자에 더 큰수를 사용할 수 있는지 확인해야 한다.
가능하면 분자를 크게하여 계산할 수 있게 작업 순서를 설정한다.
가장 좋은 방법은 높은 정밀도로 값을 변환하고 수학 연산을 수행한 후 출력에 필요한 정밀도로 변환한다.
솔리디티에는 글로벌 변수 tx.origin이 있는데 이는 call을 보낸 계정의 주소를 포함한다.
tx.origin 변수를 사용하여 사용자에게 권한을 부여하는 컨트랙트는 사용자로 하여금 취약한 컨트랙트에서 인증된 작업을 수행하도록 속일 수 있는 피싱 공격에 취약하다.
tx.origin은 스마트 컨트랙트에서 권한 설정을 위해 사용되어서는 안된다.
앞서 말했듯이 스마트 컨트랙트 보안 개발은 이슈가 많고 일일히 지키기 힘들다.
따라서 이미 구축되어졌고 검증된 라이브러리를 활용하는 것이 좋다.
특히 그 중 OpenZeppelin은 광범위한 라이브러리로 활용하기 좋다.