블로그 1뉴스 개발자 엔터프라이즈 블록 체인 설명 이벤트 및 컨퍼런스 보도 자료뉴스 레터

Contents

뉴스 레터 구독.

이메일 주소

우리는 귀하의 개인 정보를 존중합니다

홈 블로그 블록 체인 개발

스마트 계약 보안을위한 견고성 모범 사례

모니터링에서 타임 스탬프 고려 사항에 이르기까지 이더 리움 스마트 계약을 강화할 수있는 몇 가지 프로 팁이 있습니다. by ConsenSys 8 월 21, 2020Posted on 8 월 21, 2020

견고 함 모범 사례 영웅

ConsenSys Diligence, 블록 체인 보안 전문가 팀.

스마트 계약 보안 사고 방식을 마음에두고 EVM의 특이성을 파악하고 있다면 Solidity 프로그래밍 언어와 관련된 몇 가지 보안 패턴을 고려해야합니다. 이 라운드 업에서는 다른 언어로 스마트 계약을 개발하는 데 도움이 될 수있는 Solidity의 보안 개발 권장 사항에 초점을 맞출 것입니다.. 

좋아요, 뛰어 들자.

assert (), require (), revert ()를 올바로 사용하세요.

편의 기능 주장하다 과 요구하다 조건을 확인하고 조건이 충족되지 않으면 예외를 throw하는 데 사용할 수 있습니다..

그만큼 주장하다 함수는 내부 오류를 테스트하고 불변을 확인하는 데만 사용해야합니다..

그만큼 요구하다 함수는 입력 또는 계약 상태 변수와 같은 유효한 조건이 충족되는지 확인하거나 외부 계약 호출에서 반환 값의 유효성을 검사하는 데 사용해야합니다.. 

이 패러다임을 따르면 공식 분석 도구는 유효하지 않은 opcode에 도달 할 수 없음을 확인할 수 있습니다. 즉, 코드의 불변성이 위반되지 않고 코드가 공식적으로 검증되었음을 의미합니다..

pragma 견고성 ^ 0.5.0; 계약 공유자 {function sendHalf (address payable addr) public payable returns (uint balance) {require (msg.value % 2 == 0, "필요한 가치."); // Require ()는 선택적 메시지 문자열을 가질 수 있습니다. uint balanceBeforeTransfer = address (this) .balance; (bool 성공,) = addr.call.value (msg.value / 2) (""); 필요 (성공); // 이체가 실패하면 되돌 렸으므로 // 돈의 절반을 가질 방법이 없어야합니다. assert (address (this) .balance == balanceBeforeTransfer-msg.value / 2); // 내부 오류 검사에 사용됩니다. return address (this) .balance; }} 코드 언어 : JavaScript (자바 스크립트)

보다 SWC-110 & SWC-123

검사에만 수정자를 사용하십시오.

수정 자 내부의 코드는 일반적으로 함수 본문보다 먼저 실행되므로 상태 변경이나 외부 호출은 검사 효과 상호 작용 무늬. 또한 수정 자에 대한 코드가 함수 선언에서 멀어 질 수 있기 때문에 이러한 문은 개발자가 눈에 띄지 않을 수도 있습니다. 예를 들어 외부 호출 수정자는 재진입 공격으로 이어질 수 있습니다.

계약 레지스트리 {주소 소유자; function isVoter (address _addr) external returns (bool) {// Code}} contract Election {Registry registry; modifier isEligible (address _addr) {require (registry.isVoter (_addr)); _; } function vote () isEligible (msg.sender) public {// Code}} 코드 언어 : JavaScript (javascript)

이 경우 레지스트리 계약은 isVoter () 내부의 Election.vote ()를 호출하여 재진입 공격을 할 수 있습니다..

노트 : 사용하다 수정 자 isOwner ()와 같은 여러 함수에서 중복 조건 검사를 교체하려면, 그렇지 않으면 함수 내에서 필수 또는 되돌리기를 사용하세요. 이를 통해 스마트 계약 코드를 더 읽기 쉽고 감사하기 쉽습니다..

정수 나눗셈을 사용한 반올림주의

모든 정수 나누기는 가장 가까운 정수로 내림합니다. 더 많은 정밀도가 필요하면 승수를 사용하거나 분자와 분모를 모두 저장하십시오..

(향후 Solidity는 고정 소수점 입력하면이 작업이 더 쉬워집니다.)

// 잘못된 단위 x = 5/2; // 결과는 2이고 모든 정수 분할은 가장 가까운 정수로 내림합니다 .Code language : JavaScript (javascript)

승수를 사용하면 반올림을 방지 할 수 있으며 향후 x로 작업 할 때이 승수를 고려해야합니다.

// 좋은 단위 승수 = 10; uint x = (5 * multiplier) / 2; 코드 언어 : JavaScript (javascript)

분자와 분모를 저장하면 오프 체인에서 분자 / 분모의 결과를 계산할 수 있습니다.

// 좋은 단위 분자 = 5; 단위 분모 = 2; 코드 언어 : JavaScript (자바 스크립트)

사이의 장단점에 유의하십시오. 추상 계약 과 인터페이스

인터페이스와 추상 계약은 모두 스마트 계약에 대한 사용자 정의 및 재사용 가능한 접근 방식을 제공합니다. Solidity 0.4.11에 도입 된 인터페이스는 추상 계약과 유사하지만 어떤 기능도 구현할 수 없습니다. 또한 인터페이스에는 저장소에 액세스 할 수 없거나 일반적으로 추상 계약을 더 실용적으로 만드는 다른 인터페이스에서 상속 할 수없는 것과 같은 제한이 있습니다. 그러나 인터페이스는 구현 전에 계약을 설계하는 데 확실히 유용합니다. 또한 계약이 추상 계약에서 상속되는 경우 재정의를 통해 구현되지 않은 모든 기능을 구현해야하며 그렇지 않으면 추상이 될 것임을 명심하는 것이 중요합니다..

대체 기능

대체 기능을 단순하게 유지

대체 기능 계약이 인수없이 (또는 일치하는 함수가 없을 때) 메시지를 보낼 때 호출되고 .send () 또는 .transfer ()에서 호출 될 때 2,300 gas에만 액세스 할 수 있습니다. .send () 또는 .transfer ()에서 Ether를 수신하려면 대체 함수에서 수행 할 수있는 대부분의 작업은 이벤트를 기록하는 것입니다. 더 많은 가스 계산이 필요한 경우 적절한 기능을 사용하십시오..

// 잘못된 function () 지불 가능 {잔액 [msg.sender] + = msg.value; } // 좋은 함수 deposit () 지불 가능한 외부 {balances [msg.sender] + = msg.value; } function () payable {require (msg.data.length == 0); 방출 LogDepositReceived (msg.sender); } 코드 언어 : JavaScript (자바 스크립트)

대체 함수에서 데이터 길이 확인

이후 대체 기능 일반 에테르 전송 (데이터없이)뿐만 아니라 다른 함수와 일치하지 않는 경우에도 폴백 함수가 수신 된 Ether를 로깅하는 목적으로 만 사용되도록 의도 된 경우 데이터가 비어 있는지 확인해야합니다. 그렇지 않으면 호출자가 계약이 잘못 사용되어 존재하지 않는 함수가 호출되는지 알지 못합니다..

// 잘못된 function () payable {emit LogDepositReceived (msg.sender); } // 좋은 function () payable {require (msg.data.length == 0); 방출 LogDepositReceived (msg.sender); } 코드 언어 : JavaScript (자바 스크립트)

유료 함수 및 상태 변수를 명시 적으로 표시

Solidity 0.4.0부터 이더를받는 모든 함수는 지불 가능한 수정자를 사용해야합니다. 그렇지 않으면 트랜잭션에 msg.value가 있습니다. > 0은 되돌립니다 (강제 된 경우를 제외하고).

노트 : 명확하지 않은 사항 : 유료 수정자는 외부 계약의 통화에만 적용됩니다. 동일한 계약의 지불 가능 함수에서 지불 불가능 함수를 호출하면 msg.value가 여전히 설정되어 있어도 지불 불가능 함수가 실패하지 않습니다..

함수 및 상태 변수에서 가시성을 명시 적으로 표시

함수 및 상태 변수의 가시성을 명시 적으로 표시합니다. 함수는 외부, 공개, 내부 또는 비공개로 지정할 수 있습니다. 예를 들어 공개 대신 외부로 충분할 수 있습니다. 상태 변수의 경우 외부는 불가능합니다. 가시성에 레이블을 지정하면 누가 함수를 호출하거나 변수에 액세스 할 수 있는지에 대한 잘못된 가정을 쉽게 파악할 수 있습니다..

  • 외부 기능은 계약 인터페이스의 일부입니다. 외부 함수 f는 내부적으로 호출 할 수 없습니다 (예 : f ()는 작동하지 않지만 this.f ()는 작동 함). 외부 함수는 대용량 데이터를 수신 할 때 때때로 더 효율적입니다..
  • 공개 기능은 계약 인터페이스의 일부이며 내부적으로 또는 메시지를 통해 호출 할 수 있습니다. 공개 상태 변수의 경우 자동 getter 함수 (아래 참조)가 생성됩니다..
  • 내부 함수 및 상태 변수는 이것을 사용하지 않고 내부적으로 만 액세스 할 수 있습니다..
  • 비공개 함수 및 상태 변수는 파생 된 계약이 아닌 정의 된 계약에 대해서만 표시됩니다.. 노트: 계약에 포함 된 모든 것은 블록 체인 외부의 모든 관찰자에게 표시되며 비공개 변수도 볼 수 있습니다..

// 잘못된 단위 x; // 기본값은 상태 변수에 대한 내부이지만 명시 적으로 만들어야합니다. function buy () {// 기본값은 public입니다. // public code} // good uint private y; function buy () external {// 외부 적으로 만 호출 가능하거나 this.buy ()를 사용하여} function utility () public {// 내부적으로는 물론 외부 적으로 호출 가능 :이 코드를 변경하려면 두 경우 모두에 대해 생각해야합니다. } function internalAction () internal {// 내부 코드} 코드 언어 : PHP (php)

보다 SWC-100 과 SWC-108

pragma를 특정 컴파일러 버전에 고정

계약은 가장 많이 테스트 된 것과 동일한 컴파일러 버전 및 플래그를 사용하여 배포해야합니다. pragma를 잠그면 발견되지 않은 버그의 위험이 더 높은 최신 컴파일러를 사용하여 계약이 실수로 배포되는 것을 방지 할 수 있습니다. 계약은 다른 사람이 배포 할 수도 있으며 pragma는 원래 작성자가 의도 한 컴파일러 버전을 나타냅니다..

// 나쁜 pragma 견고 함 ^ 0.4.4; // 좋은 pragma 견고 함 0.4.4; 코드 언어 : JavaScript (javascript)

참고 : 플로팅 pragma 버전 (예 : ^ 0.4.25)은 0.4.26-nightly.2018.9.25로 잘 컴파일되지만 프로덕션 용 코드를 컴파일하는 데 야간 빌드를 사용해서는 안됩니다..

경고: Pragma 문은 라이브러리 또는 EthPM 패키지의 계약에서와 같이 다른 개발자가 계약을 사용하려는 경우 부동 할 수 있습니다. 그렇지 않으면 개발자가 로컬에서 컴파일하기 위해 pragma를 수동으로 업데이트해야합니다..

보다 SWC-103

이벤트를 사용하여 계약 활동 모니터링

계약이 배포 된 후 계약의 활동을 모니터링하는 방법이 있으면 유용 할 수 있습니다. 이를 수행하는 한 가지 방법은 계약의 모든 트랜잭션을 보는 것입니다. 그러나 계약 간의 메시지 호출이 블록 체인에 기록되지 않기 때문에 충분하지 않을 수 있습니다. 또한 상태에 대한 실제 변경이 아닌 입력 매개 변수 만 표시합니다. 또한 이벤트를 사용하여 사용자 인터페이스에서 기능을 트리거 할 수 있습니다..

계약 자선 {mapping (address => uint) 균형; function donate () payable public {balances [msg.sender] + = msg.value; }} 계약 게임 {function buyCoins () payable public {// 5 %는 charity.donate.value (msg.value / 20) (); }} 코드 언어 : JavaScript (자바 스크립트)

여기에서 게임 계약은 Charity.donate ()를 내부적으로 호출합니다. 이 거래는 자선 단체의 외부 거래 목록에는 표시되지 않지만 내부 거래에만 표시됩니다..

이벤트는 계약에서 발생한 일을 기록하는 편리한 방법입니다. 생성 된 이벤트는 다른 계약 데이터와 함께 블록 체인에 남아 있으며 향후 감사에 사용할 수 있습니다. 다음은 자선 단체의 기부 내역을 제공하는 이벤트를 사용하여 위의 예를 개선 한 것입니다..

contract Charity {// 이벤트 이벤트 정의 LogDonate (uint _amount); 매핑 (주소 => uint) 균형; function donate () payable public {balances [msg.sender] + = msg.value; // 이벤트 방출 LogDonate (msg.value); }} 계약 게임 {function buyCoins () payable public {// 5 %는 charity.donate.value (msg.value / 20) (); }} 코드 언어 : JavaScript (자바 스크립트)

여기에서 직접적이든 아니든 자선 계약을 거치는 모든 거래는 기부 금액과 함께 해당 계약의 이벤트 목록에 표시됩니다..

참고 : 최신 Solidity 구성 선호. selfdestruct (자살보다) 및 keccak256 (sha3보다)과 같은 구문 / 별칭을 선호합니다. require (msg.sender.send (1 ether))와 같은 패턴은 msg.sender.transfer (1 ether)에서와 같이 transfer ()를 사용하여 단순화 할 수도 있습니다. 체크 아웃 견고성 변경 로그 더 유사한 변화를 위해.

‘내장’은 그림자가 될 수 있습니다.

현재 가능합니다 그림자 Solidity에 내장 된 글로벌. 이렇게하면 계약이 msg 및 revert ()와 같은 내장 기능을 재정의 할 수 있습니다. 이건 의도되었다, 계약의 실제 행동에 대해 계약 사용자를 오도 할 수 있습니다..

계약 PretendingToRevert {function revert () 내부 상수 {}} 계약 ExampleContract는 PretendingToRevert {function somethingBad () public {revert (); }}

계약 사용자 (및 감사 자)는 사용하려는 모든 애플리케이션의 전체 스마트 계약 소스 코드를 알고 있어야합니다..

tx.origin 사용 금지

승인을 위해 tx.origin을 사용하지 마세요. 다른 계약은 귀하의 계약을 호출하는 방법을 가질 수 있으며 (예를 들어 사용자에게 자금이있는 경우) 귀하의 주소가 tx.origin에 있으므로 계약에서 해당 거래를 승인합니다..

계약 MyContract {주소 소유자; function MyContract () public {owner = msg.sender; } function sendTo (주소 수신자, 단위 금액) public {require (tx.origin == owner); (bool 성공,) = receiver.call.value (amount) (""); 필요 (성공); }} 계약 AttackingContract {MyContract myContract; 주소 공격자 function AttackingContract (address myContractAddress) public {myContract = MyContract (myContractAddress); 공격자 = msg.sender; } function () public {myContract.sendTo (attacker, msg.sender.balance); }} 코드 언어 : JavaScript (자바 스크립트)

승인을 위해 msg.sender를 사용해야합니다 (다른 계약에서 계약을 호출하는 경우 msg.sender는 계약을 호출 한 사용자의 주소가 아니라 계약의 주소가 됨)..

여기에서 자세한 내용을 읽을 수 있습니다. Solidity 문서

경고: 승인 문제 외에도 향후 이더 리움 프로토콜에서 tx.origin이 제거 될 가능성이 있으므로 tx.origin을 사용하는 코드는 향후 릴리스와 호환되지 않습니다. Vitalik :‘tx.origin이 계속 사용 가능하거나 의미가있을 것이라고 가정하지 마십시오.’

또한 tx.origin을 사용하면 tx.origin을 사용하는 계약이 다른 계약에서 tx.origin이 될 수 없기 때문에 계약 간의 상호 운용성이 제한된다는 점도 언급 할 가치가 있습니다..

보다 SWC-115

타임 스탬프 의존성

타임 스탬프를 사용하여 계약에서 중요한 기능을 실행할 때, 특히 자금 이체와 관련된 작업의 경우 세 가지 주요 고려 사항이 있습니다..

타임 스탬프 조작

블록의 타임 스탬프는 채굴자가 조작 할 수 있습니다. 이걸 고려하세요 계약:

uint256 상수 개인 솔트 = block.timestamp; function random (uint Max) constant private returns (uint256 result) {// 무작위성에 대한 최상의 시드 가져 오기 uint256 x = salt * 100 / Max; uint256 y = salt * block.number / (salt % 5); uint256 seed = block.number / 3 + (salt % 300) + Last_Payout + y; uint256 h = uint256 (block.blockhash (seed)); return uint256 ((h / x)) % Max + 1; // 1과 최대 사이의 난수} 코드 언어 : PHP (php)

계약이 타임 스탬프를 사용하여 임의의 숫자를 시드 할 때 채굴자는 실제로 블록이 검증 된 후 15 초 이내에 타임 스탬프를 게시하여 채굴자가 복권 기회에 더 유리한 옵션을 미리 계산할 수 있습니다. 타임 스탬프는 무작위가 아니며 해당 컨텍스트에서 사용해서는 안됩니다..

15 초 규칙

그만큼 노란 종이 (Ethereum의 참조 사양)은 시간에 따라 얼마나 많은 블록이 표류 할 수 있는지에 대한 제약을 지정하지 않지만 그것은 지정하지 않습니다 각 타임 스탬프는 상위 타임 스탬프보다 커야합니다. 인기있는 이더 리움 프로토콜 구현 게스 과 동등 둘 다 미래에 15 초 이상의 타임 스탬프가있는 블록을 거부합니다. 따라서 타임 스탬프 사용을 평가할 때 좋은 경험 법칙은 시간 종속 이벤트의 규모가 15 초까지 다양하고 무결성을 유지할 수있는 경우 block.timestamp를 사용하는 것이 안전하다는 것입니다..

block.number를 타임 스탬프로 사용하지 마세요.

block.number 속성을 사용하여 시간 델타를 추정 할 수 있습니다. 평균 블록 시간, 그러나 이것은 블록 시간이 변경 될 수 있으므로 (예 : 포크 개편 그리고 난이도 폭탄). 며칠에 걸친 판매에서 15 초 규칙을 사용하면보다 신뢰할 수있는 예상 시간을 얻을 수 있습니다..

보다 SWC-116

다중 상속주의

Solidity에서 다중 상속을 사용할 때 컴파일러가 상속 그래프를 구성하는 방법을 이해하는 것이 중요합니다..

계약 최종 {uint public a; function Final (uint f) public {a = f; }} 계약 B는 최종 {int public fee; function B (uint f) Final (f) public {} function setFee () public {fee = 3; }} 계약 C는 최종 {int public fee; function C (uint f) Final (f) public {} function setFee () public {fee = 5; }} 계약 A는 B, C {function A () public B (3) C (5) {setFee (); }} 코드 언어 : PHP (php)

계약이 배포되면 컴파일러는 상속을 오른쪽에서 왼쪽으로 선형화합니다 (키워드는 상위 항목이 가장 기본적인 항목에서 가장 많이 파생 된 항목으로 나열된 후). 계약 A의 선형화는 다음과 같습니다.

결정적인 <- 비 <- 씨 <- ㅏ

C가 가장 많이 파생 된 계약이므로 선형화의 결과로 수수료 값 5가 산출됩니다. 이것은 분명해 보일 수 있지만 C가 중요한 기능을 숨기고 부울 절을 재정렬하고 개발자가 악용 가능한 계약을 작성하도록 할 수있는 시나리오를 상상해보십시오. 현재 정적 분석은 그림자가있는 함수에 문제를 일으키지 않으므로 수동으로 검사해야합니다..

기여를 돕기 위해 Solidity의 Github는 계획 모든 상속 관련 문제.

보다 SWC-125

유형 안전을 위해 주소 대신 인터페이스 유형 사용

함수가 계약 주소를 인수로 사용하는 경우 원시 주소보다는 인터페이스 또는 계약 유형을 전달하는 것이 좋습니다. 함수가 소스 코드 내의 다른 곳에서 호출되는 경우 컴파일러는 추가 형식 안전 보장을 제공합니다..

여기에 두 가지 대안이 있습니다.

계약 유효성 검사기 {function validate (uint) external returns (bool); } contract TypeSafeAuction {// 좋은 기능 validateBet (Validator _validator, uint _value) 내부 returns (bool) {bool valid = _validator.validate (_value); 유효한 반환; }} contract TypeUnsafeAuction {// 잘못된 함수 validateBet (address _addr, uint _value) internal returns (bool) {Validator validator = Validator (_addr); bool valid = validator.validate (_value); 유효한 반환; }} 코드 언어 : JavaScript (자바 스크립트)

위의 TypeSafeAuction 계약 사용의 이점은 다음 예에서 확인할 수 있습니다. 주소 인수 또는 유효성 검사기 이외의 계약 유형을 사용하여 validateBet ()을 호출하면 컴파일러에서 다음 오류가 발생합니다.

계약 NonValidator {} 계약 경매는 TypeSafeAuction {NonValidator nonValidator입니다. function bet (uint _value) {bool valid = validateBet (nonValidator, _value); // TypeError : 함수 호출의 인수 유형이 잘못되었습니다. // 계약 NonValidator에서 계약 Validator 로의 잘못된 암시 적 변환이 요청되었습니다. }} 코드 언어 : JavaScript (자바 스크립트)

외부 소유 계정을 확인하기 위해 extcodesize를 사용하지 마세요.

다음 수정 자 (또는 유사한 검사)는 호출이 외부 소유 계정 (EOA) 또는 계약 계정에서 이루어 졌는지 확인하는 데 자주 사용됩니다.

// 잘못된 수정 자 isNotContract (address _a) {uint size; 어셈블리 {크기 : = extcodesize (_a)} require (size == 0); _; } 코드 언어 : JavaScript (자바 스크립트)

아이디어는 간단합니다. 주소에 코드가 포함되어 있으면 EOA가 아니라 계약 계정입니다. 하나, 컨트랙트에 구성 중에 사용할 수있는 소스 코드가 없습니다.. 즉, 생성자가 실행되는 동안 다른 계약을 호출 할 수 있지만 해당 주소에 대한 extcodesize는 0을 반환합니다. 다음은이 검사를 피할 수있는 방법을 보여주는 최소한의 예입니다.

계약 OnlyForEOA {공용 플래그 단위; // 잘못된 수정 자 isNotContract (address _a) {uint len; 어셈블리 {len : = extcodesize (_a)} require (len == 0); _; } function setFlag (uint i) public isNotContract (msg.sender) {flag = i; }} 계약 FakeEOA {constructor (address _a) public {OnlyForEOA c = OnlyForEOA (_a); c.setFlag (1); }} 코드 언어 : JavaScript (자바 스크립트)

계약 주소는 미리 계산 될 수 있기 때문에 블록 n에서 비어 있지만 n보다 큰 블록에서 계약이 배포 된 주소를 확인하는 경우에도이 확인이 실패 할 수 있습니다..

경고: 이 문제는 미묘합니다. 다른 계약에서 계약을 호출 할 수 없도록하는 것이 목표라면 extcodesize 확인만으로 충분할 것입니다. 다른 방법은 (tx.origin == msg.sender)의 값을 확인하는 것입니다. 단점이있다.

extcodesize 검사가 목적에 부합하는 다른 상황이있을 수 있습니다. 여기에 모두 설명하는 것은 범위를 벗어납니다. EVM의 기본 동작을 이해하고 판단을 사용하십시오..

귀하의 블록 체인 코드는 안전합니까??

보안 전문가와 함께 1 일 현장 확인을 예약하세요. 오늘 예약하세요 근면 보안 스마트 계약 솔리 디티 뉴스 레터 최신 이더 리움 뉴스, 엔터프라이즈 솔루션, 개발자 리소스 등에 대한 뉴스 레터를 구독하십시오. 이메일 주소 독점 콘텐츠성공적인 블록 체인 제품을 구축하는 방법웨비나

성공적인 블록 체인 제품을 구축하는 방법

이더 리움 노드를 설정하고 실행하는 방법웨비나

이더 리움 노드를 설정하고 실행하는 방법

나만의 Ethereum API를 구축하는 방법웨비나

나만의 Ethereum API를 구축하는 방법

소셜 토큰을 만드는 방법웨비나

소셜 토큰을 만드는 방법

스마트 계약 개발에서 보안 도구 사용웨비나

스마트 계약 개발에서 보안 도구 사용

금융 디지털 자산 및 DeFi의 미래웨비나

금융의 미래 : 디지털 자산 및 DeFi

Mike Owergreen Administrator
Sorry! The Author has not filled his profile.
follow me