คำแนะนำด้านความปลอดภัย Ethereum Smart Contract

บล็อก 1NewsDevelopersEnterpriseBlockchain ExplainedEvents and ConferencesPressจดหมายข่าว

Contents

สมัครรับจดหมายข่าวของเรา.

ที่อยู่อีเมล

เราเคารพความเป็นส่วนตัวของคุณ

หน้าแรกบล็อกการพัฒนา Blockchain

คำแนะนำด้านความปลอดภัย Ethereum Smart Contract

ตั้งแต่วิธีจัดการการโทรภายนอกไปจนถึงแผนการผูกมัดนี่คือรูปแบบการรักษาความปลอดภัยของสัญญาอัจฉริยะ 10+ รูปแบบที่คุณต้องปฏิบัติตามเมื่อคุณสร้างบน Ethereum โดย ConsenSys กรกฎาคม 10, 2020 โพสต์เมื่อกรกฎาคม 10, 2020

คำแนะนำด้านความปลอดภัย Ethereum Smart Contract


ดังที่เรากล่าวถึงใน Smart Contract Security Mindset นักพัฒนา Ethereum ที่กระตือรือร้นมักจะคำนึงถึงหลัก 5 ประการดังต่อไปนี้:

  • เตรียมพร้อมสำหรับความล้มเหลว
  • เปิดตัวอย่างระมัดระวัง
  • ทำสัญญาง่ายๆ
  • ติดตามข่าวสารล่าสุด
  • ตระหนักถึงความแปลกประหลาดของ EVM

ในโพสต์นี้เราจะเจาะลึกความแตกต่างของ EVM และอธิบายรายชื่อรูปแบบที่คุณควรปฏิบัติตามเมื่อพัฒนาระบบสัญญาอัจฉริยะบน Ethereum งานชิ้นนี้มีไว้สำหรับนักพัฒนา Ethereum ระดับกลางเป็นหลัก หากคุณยังอยู่ในช่วงเริ่มต้นของการสำรวจโปรดดูโปรแกรมสำหรับนักพัฒนาบล็อกเชนตามความต้องการของ ConsenSys Academy. 

เอาล่ะมาดำน้ำกัน.

การโทรภายนอก

ใช้ความระมัดระวังเมื่อโทรออกภายนอก

การโทรไปหาสัญญาอัจฉริยะที่ไม่น่าเชื่อถืออาจทำให้เกิดความเสี่ยงหรือข้อผิดพลาดที่ไม่คาดคิดได้หลายประการ การโทรภายนอกอาจเรียกใช้โค้ดที่เป็นอันตรายในสัญญานั้นหรือสัญญาอื่น ๆ ที่ขึ้นอยู่กับ ดังนั้นให้ถือว่าการโทรภายนอกทุกครั้งเป็นความเสี่ยงด้านความปลอดภัยที่อาจเกิดขึ้น เมื่อเป็นไปไม่ได้หรือไม่พึงปรารถนาที่จะลบการโทรภายนอกให้ใช้คำแนะนำในส่วนที่เหลือเพื่อลดอันตรายให้น้อยที่สุด.

ทำเครื่องหมายสัญญาที่ไม่น่าเชื่อถือ

เมื่อโต้ตอบกับสัญญาภายนอกให้ตั้งชื่อตัวแปรวิธีการและอินเทอร์เฟซสัญญาในลักษณะที่ทำให้ชัดเจนว่าการโต้ตอบกับสัญญาอาจไม่ปลอดภัย สิ่งนี้ใช้กับฟังก์ชันของคุณเองที่เรียกสัญญาภายนอก.

// Bank.withdraw ไม่ดี (100); // ไม่ชัดเจนว่าฟังก์ชันที่เชื่อถือได้หรือไม่น่าเชื่อถือ makeWithdrawal (จำนวนเงิน uint) {// ไม่ชัดเจนว่าฟังก์ชันนี้อาจไม่ปลอดภัย Bank.withdraw (จำนวนเงิน); } // ดี UntrustedBank.withdraw (100); // สายภายนอกที่ไม่น่าเชื่อถือ TrustedBank.withdraw (100); // สัญญาธนาคารภายนอก แต่เชื่อถือได้ที่ดูแลโดยฟังก์ชัน XYZ Corp makeUntrustedWithdrawal (uint amount) {UntrustedBank.withdraw (amount); } ภาษารหัส: PHP (php)

หลีกเลี่ยงการเปลี่ยนแปลงสถานะหลังจากการโทรภายนอก

ไม่ว่าจะใช้การโทรดิบ (ในรูปแบบ someAddress.call ()) หรือการเรียกตามสัญญา (ในรูปแบบ ExternalContract.someMethod ()) ให้สมมติว่าโค้ดที่เป็นอันตรายอาจทำงานได้ แม้ว่า ExternalContract จะไม่เป็นอันตราย แต่โค้ดที่เป็นอันตรายก็สามารถดำเนินการได้โดยสัญญาใด ๆ ที่เรียกใช้.

อันตรายอย่างหนึ่งคือโค้ดที่เป็นอันตรายอาจขัดขวางขั้นตอนการควบคุมซึ่งนำไปสู่ช่องโหว่เนื่องจากการกลับเข้ามาใหม่ (ดู Reentrancy สำหรับการอภิปรายปัญหานี้อย่างเต็มที่).

หากคุณกำลังโทรไปยังสัญญาภายนอกที่ไม่น่าเชื่อถือให้หลีกเลี่ยงการเปลี่ยนแปลงสถานะหลังการโทร รูปแบบนี้บางครั้งเรียกว่า รูปแบบการตรวจสอบผลกระทบ – ปฏิสัมพันธ์.

ดู SWC-107

อย่าใช้การโอน () หรือส่ง ().

.โอน () และส่ง () ส่งต่อก๊าซ 2,300 ไปยังผู้รับ เป้าหมายของค่าจ้างก๊าซที่เข้ารหัสนี้คือเพื่อป้องกัน ช่องโหว่การย้อนกลับ, แต่สิ่งนี้สมเหตุสมผลภายใต้สมมติฐานที่ว่าต้นทุนก๊าซคงที่. EIP 1884, ซึ่งเป็นส่วนหนึ่งของฮาร์ดฟอร์กของอิสตันบูลทำให้ต้นทุนก๊าซของการดำเนินการ SLOAD เพิ่มขึ้น สิ่งนี้ทำให้ฟังก์ชันทางเลือกของสัญญามีค่าใช้จ่ายมากกว่า 2300 ก๊าซ เราขอแนะนำให้หยุดใช้.transfer () and.send () และใช้แทน use.call ().

// สัญญาที่ไม่ดีช่องโหว่ {ฟังก์ชั่นถอน (จำนวน uint256) ภายนอก {// ส่งต่อ 2300 ก๊าซซึ่งอาจไม่เพียงพอหากผู้รับ // เป็นสัญญาและต้นทุนก๊าซเปลี่ยนแปลง msg.sender.transfer (จำนวนเงิน); }} // สัญญาที่ดีคงที่ {functiondraw (uint256 จำนวน) ภายนอก {// ส่งต่อก๊าซที่มีอยู่ทั้งหมด อย่าลืมตรวจสอบมูลค่าการคืนสินค้า! (ความสำเร็จบูล) = msg.sender.call.value (จำนวนเงิน) (""); ต้องการ (ความสำเร็จ, "การโอนล้มเหลว."); }} รหัสภาษา: JavaScript (javascript)

โปรดทราบว่า.call () ไม่ได้ทำอะไรเพื่อลดการโจมตีซ้ำดังนั้นจึงต้องใช้ความระมัดระวังอื่น ๆ เพื่อป้องกันการโจมตีซ้ำให้ใช้ไฟล์ รูปแบบการตรวจสอบผลกระทบ – ปฏิสัมพันธ์.

จัดการข้อผิดพลาดในการโทรภายนอก

Solidity นำเสนอวิธีการโทรระดับต่ำที่ทำงานกับที่อยู่ดิบ: address.call (), address.callcode (), address.delegatecall () และ address.send () วิธีการระดับต่ำเหล่านี้ไม่เคยทำให้เกิดข้อยกเว้น แต่จะส่งคืนเท็จหากการเรียกพบข้อยกเว้น ในทางกลับกันการโทรตามสัญญา (เช่น ExternalContract.doSomething ()) จะแพร่กระจายการโยนโดยอัตโนมัติ (ตัวอย่างเช่น ExternalContract.doSomething () จะโยนหาก doSomething () พ่น).

หากคุณเลือกใช้วิธีการโทรระดับต่ำตรวจสอบให้แน่ใจว่าได้จัดการกับความเป็นไปได้ที่การโทรจะล้มเหลวโดยตรวจสอบค่าส่งคืน.

// ไม่ดี someAddress.send (55); someAddress.call.value (55) (""); // สิ่งนี้อันตรายเป็นสองเท่าเนื่องจากจะส่งต่อก๊าซที่เหลือทั้งหมดและไม่ตรวจสอบผลลัพธ์ someAddress.call.value (100) (bytes4 (sha3 ("เงินฝาก ()"))); // หากการฝากส่งข้อยกเว้นการโทรดิบ () จะส่งคืนเฉพาะเท็จและธุรกรรมจะไม่ถูกเปลี่ยนกลับ // good (bool success,) = someAddress.call.value (55) (""); if (! success) {// จัดการรหัสความล้มเหลว} ExternalContract (someAddress) .deposit.value (100) (); ภาษารหัส: JavaScript (javascript)

ดู SWC-104

ความชื่นชอบดึงการผลักดันสำหรับการโทรภายนอก

การโทรภายนอกอาจล้มเหลวโดยไม่ตั้งใจหรือโดยเจตนา เพื่อลดความเสียหายที่เกิดจากความล้มเหลวดังกล่าวมักจะเป็นการดีกว่าที่จะแยกการโทรภายนอกแต่ละครั้งออกเป็นธุรกรรมของตัวเองที่ผู้รับสายสามารถเริ่มต้นได้ สิ่งนี้เกี่ยวข้องกับการชำระเงินโดยเฉพาะซึ่งจะเป็นการดีกว่าที่จะให้ผู้ใช้ถอนเงินแทนที่จะส่งเงินไปให้โดยอัตโนมัติ (นอกจากนี้ยังช่วยลดโอกาสของ ปัญหาเกี่ยวกับขีด จำกัด ก๊าซ.) หลีกเลี่ยงการรวมการโอนอีเธอร์หลายรายการในธุรกรรมเดียว.

// ประมูลสัญญาไม่ดี {address maximumBidder; uint maximumBid; function bid () payable {ต้องใช้ (msg.value >= maximumBid); ถ้า (maximumBidder! = address (0)) {(bool success,) = maximumBidder.call.value (maximumBid) (""); ต้องการ (ความสำเร็จ); // หากการโทรนี้ล้มเหลวอย่างต่อเนื่องจะไม่มีใครเสนอราคา} maximumBidder = msg.sender; maximumBid = msg.value; }} // ประมูลสัญญาดี {address maximumBidder; uint maximumBid; การทำแผนที่ (ที่อยู่ => uint) การคืนเงิน; function bid () เจ้าหนี้ภายนอก {ต้องใช้ (msg.value >= maximumBid); ถ้า (maximumBidder! = address (0)) {คืนเงิน [maximumBidder] + = maximumBid; // บันทึกการคืนเงินที่ผู้ใช้รายนี้เรียกร้องได้} maximumBidder = msg.sender; maximumBid = msg.value; } functiondrawRefund () ภายนอก {uint refund = การคืนเงิน [msg.sender]; การคืนเงิน [msg.sender] = 0; (bool success,) = msg.sender.call.value (คืนเงิน) (""); ต้องการ (ความสำเร็จ); }} รหัสภาษา: JavaScript (javascript)

ดู SWC-128

อย่ามอบหมายให้โทรหารหัสที่ไม่น่าเชื่อถือ

ฟังก์ชัน delegatecall เรียกฟังก์ชันจากสัญญาอื่น ๆ ราวกับว่าเป็นของสัญญาผู้โทร ดังนั้นผู้เรียกอาจเปลี่ยนสถานะของที่อยู่ที่โทร ซึ่งอาจไม่ปลอดภัย ตัวอย่างด้านล่างแสดงให้เห็นว่าการใช้ delegatecall สามารถนำไปสู่การทำลายสัญญาและการสูญเสียยอดคงเหลือได้อย่างไร.

ตัวทำลายสัญญา {function doWork () ภายนอก {selfdestruct (0); }} ผู้ปฏิบัติงานตามสัญญา {function doWork (ที่อยู่ _internalWorker) สาธารณะ {// ไม่ปลอดภัย _internalWorker.delegatecall (bytes4 (keccak256 ("ทำงาน()"))); }} รหัสภาษา: JavaScript (javascript)

ถ้า Worker.doWork () ถูกเรียกโดยใช้ที่อยู่ของสัญญา Destructor ที่ปรับใช้เป็นอาร์กิวเมนต์สัญญา Worker จะทำลายตัวเอง มอบหมายการดำเนินการเฉพาะกับสัญญาที่เชื่อถือได้และ ไม่เคยไปยังที่อยู่ที่ผู้ใช้ระบุ.

คำเตือน

อย่าถือว่าสัญญาสร้างขึ้นโดยมียอดคงเหลือเป็นศูนย์ ผู้โจมตีสามารถส่งอีเธอร์ไปยังที่อยู่ของสัญญาก่อนที่จะสร้างขึ้น สัญญาไม่ควรถือว่าสถานะเริ่มต้นมียอดคงเหลือเป็นศูนย์ ดู ฉบับที่ 61 สำหรับรายละเอียดเพิ่มเติม.

ดู SWC-112

โปรดจำไว้ว่าอีเธอร์สามารถบังคับให้ส่งไปยังบัญชีได้

ระวังการเข้ารหัสค่าคงที่ที่ตรวจสอบยอดคงเหลือของสัญญาอย่างเคร่งครัด.

ผู้โจมตีสามารถบังคับให้ส่งอีเธอร์ไปยังบัญชีใดก็ได้ สิ่งนี้ไม่สามารถป้องกันได้ (แม้จะมีฟังก์ชันทางเลือกที่ย้อนกลับ ()).

ผู้โจมตีสามารถทำได้โดยการสร้างสัญญาระดมทุนด้วย 1 wei และเรียกใช้ selfdestruct (เหยื่อที่อยู่) ไม่มีการเรียกใช้รหัสในเหยื่อที่อยู่ดังนั้นจึงไม่สามารถป้องกันได้ นอกจากนี้ยังเป็นจริงสำหรับรางวัลบล็อกซึ่งส่งไปยังที่อยู่ของคนงานเหมืองซึ่งอาจเป็นที่อยู่ใดก็ได้.

นอกจากนี้เนื่องจากที่อยู่ในสัญญาสามารถคำนวณล่วงหน้าได้จึงสามารถส่งอีเธอร์ไปยังที่อยู่ก่อนที่สัญญาจะถูกทำให้ใช้งานได้.

ดู SWC-132

โปรดจำไว้ว่าข้อมูลออนไลน์เป็นแบบสาธารณะ

แอปพลิเคชันจำนวนมากต้องการให้ข้อมูลที่ส่งเป็นส่วนตัวจนถึงเวลาที่ใช้งานได้ เกม (เช่นเป่ายิ้งฉุบแบบออนโซ่) และกลไกการประมูล (เช่นการประมูลแบบปิดผนึก การประมูล Vickrey) เป็นตัวอย่างสองประเภทหลัก ๆ หากคุณกำลังสร้างแอปพลิเคชันที่มีปัญหาเรื่องความเป็นส่วนตัวตรวจสอบให้แน่ใจว่าคุณหลีกเลี่ยงการกำหนดให้ผู้ใช้เผยแพร่ข้อมูลเร็วเกินไป กลยุทธ์ที่ดีที่สุดคือการใช้ แผนการมุ่งมั่น ด้วยเฟสที่แยกจากกัน: คอมมิตแรกโดยใช้แฮชของค่าและในเฟสต่อมาจะเปิดเผยค่า.

ตัวอย่าง:

  • ในการเป่ายิ้งฉุบกำหนดให้ผู้เล่นทั้งสองส่งแฮชของการเคลื่อนไหวที่ตั้งใจไว้ก่อนจากนั้นกำหนดให้ผู้เล่นทั้งสองคนส่งการเคลื่อนไหว หากการย้ายที่ส่งไม่ตรงกับแฮชก็โยนมันออกไป.
  • ในการประมูลกำหนดให้ผู้เล่นส่งแฮชของมูลค่าการเสนอราคาในระยะเริ่มต้น (พร้อมกับเงินฝากที่มากกว่ามูลค่าการเสนอราคา) จากนั้นจึงส่งมูลค่าการประมูลในขั้นตอนที่สอง.
  • เมื่อพัฒนาแอปพลิเคชันที่ขึ้นอยู่กับตัวสร้างตัวเลขสุ่มลำดับควรเป็น (1) ผู้เล่นส่งการเคลื่อนไหว (2) สร้างหมายเลขสุ่ม (3) ผู้เล่นที่จ่ายเงิน หลายคนกำลังค้นคว้าเกี่ยวกับเครื่องกำเนิดตัวเลขแบบสุ่ม โซลูชันที่ดีที่สุดในระดับเดียวกันในปัจจุบัน ได้แก่ ส่วนหัวบล็อก Bitcoin (ตรวจสอบผ่าน http://btcrelay.org) แผนการเปิดเผยแฮช (กล่าวคือฝ่ายหนึ่งสร้างตัวเลขเผยแพร่แฮชของตนเพื่อ “กระทำ” กับมูลค่าจากนั้นจึงเปิดเผยค่าในภายหลัง) และ รานดาว. เนื่องจาก Ethereum เป็นโปรโตคอลที่กำหนดคุณจึงไม่สามารถใช้ตัวแปรใด ๆ ภายในโปรโตคอลเป็นตัวเลขสุ่มที่คาดเดาไม่ได้ โปรดทราบว่าคนงานเหมืองอยู่ในระดับหนึ่งในการควบคุมค่า block.blockhash ()*.

ระวังความเป็นไปได้ที่ผู้เข้าร่วมบางคนอาจ “ออกจากระบบออฟไลน์” และไม่กลับมาอีก

อย่าทำการคืนเงินหรือกระบวนการเรียกร้องโดยขึ้นอยู่กับฝ่ายใดฝ่ายหนึ่งที่ดำเนินการบางอย่างโดยไม่มีวิธีอื่นในการดึงเงินออก ตัวอย่างเช่นในเกมเป่ายิ้งฉุบข้อผิดพลาดทั่วไปอย่างหนึ่งคืออย่าจ่ายเงินจนกว่าผู้เล่นทั้งสองฝ่ายจะยอมเคลื่อนไหว อย่างไรก็ตามผู้เล่นที่ประสงค์ร้ายสามารถ“ เศร้าโศก” อีกฝ่ายได้โดยเพียงแค่ไม่ส่งการเคลื่อนไหวของพวกเขา – ในความเป็นจริงหากผู้เล่นเห็นการเคลื่อนไหวที่เปิดเผยของผู้เล่นคนอื่นและตัดสินว่าพวกเขาแพ้พวกเขาก็ไม่มีเหตุผลที่จะส่งการเคลื่อนไหวของตัวเองเลย ปัญหานี้อาจเกิดขึ้นในบริบทของการยุติช่องสัญญาณของรัฐ เมื่อสถานการณ์ดังกล่าวเป็นปัญหา (1) จัดเตรียมวิธีการหลีกเลี่ยงผู้เข้าร่วมที่ไม่เข้าร่วมโดยอาจใช้เวลา จำกัด และ (2) พิจารณาเพิ่มแรงจูงใจทางเศรษฐกิจเพิ่มเติมสำหรับผู้เข้าร่วมในการส่งข้อมูลในทุกสถานการณ์ที่พวกเขาอยู่ ควรจะทำเช่นนั้น.

ระวังการปฏิเสธของจำนวนเต็มที่ลงนามที่เป็นลบมากที่สุด

Solidity มีหลายประเภทในการทำงานกับจำนวนเต็มที่ลงนาม เช่นเดียวกับภาษาโปรแกรมส่วนใหญ่ใน Solidity จำนวนเต็มที่ลงนามด้วย N บิตสามารถแทนค่าได้ตั้งแต่ -2 ^ (N-1) ถึง 2 ^ (N-1) -1 ซึ่งหมายความว่าไม่มีค่าเท่ากับค่าบวกสำหรับ MIN_INT การลบจะใช้เป็นการค้นหาส่วนเติมเต็มของตัวเลขทั้งสองดังนั้นการปฏิเสธของจำนวนลบส่วนใหญ่ จะส่งผลให้ได้หมายเลขเดียวกัน. นี่เป็นจริงสำหรับประเภทจำนวนเต็มที่ลงนามทั้งหมดใน Solidity (int8, int16, …, int256).

การปฏิเสธสัญญา {function negate8 (int8 _i) public pure return (int8) {return -_i; } function negate16 (int16 _i) public pure return (int16) {return -_i; } int8 สาธารณะ a = negate8 (-128); // -128 int16 สาธารณะ b = negate16 (-128); // 128 int16 สาธารณะ c = negate16 (-32768); // -32768} ภาษารหัส: PHP (php)

วิธีหนึ่งในการจัดการสิ่งนี้คือตรวจสอบค่าของตัวแปรก่อนที่จะปฏิเสธและโยนว่ามันเท่ากับ MIN_INT หรือไม่ อีกทางเลือกหนึ่งคือเพื่อให้แน่ใจว่าจำนวนลบส่วนใหญ่จะไม่เกิดขึ้นโดยใช้ประเภทที่มีความจุสูงกว่า (เช่น int32 แทน int16).

ปัญหาที่คล้ายกันกับประเภท int เกิดขึ้นเมื่อ MIN_INT คูณหรือหารด้วย -1.

รหัส blockchain ของคุณปลอดภัยหรือไม่? 

เราหวังว่าคำแนะนำเหล่านี้จะเป็นประโยชน์ หากคุณและทีมของคุณกำลังเตรียมพร้อมสำหรับการเปิดตัวหรือแม้กระทั่งในช่วงเริ่มต้นของวงจรการพัฒนาและต้องการการตรวจสอบความถูกต้องของสัญญาอัจฉริยะโปรดติดต่อทีมวิศวกรด้านความปลอดภัยของเราที่ ConsenSys Diligence เราพร้อมช่วยคุณในการเปิดตัวและดูแลแอปพลิเคชัน Ethereum ของคุณด้วยความมั่นใจ 100%. 

จองการตรวจสอบความปลอดภัย

จองการตรวจสอบ 1 วันกับทีมผู้เชี่ยวชาญด้านความปลอดภัยบล็อกเชนของเรา จองวันนี้ความปลอดภัยสัญญาอัจฉริยะจดหมายข่าวสมัครรับจดหมายข่าวของเราเพื่อรับข่าวสารล่าสุดของ Ethereum โซลูชันระดับองค์กรทรัพยากรสำหรับนักพัฒนาและอื่น ๆ ที่อยู่อีเมลเนื้อหาพิเศษวิธีสร้างผลิตภัณฑ์ Blockchain ที่ประสบความสำเร็จการสัมมนาผ่านเว็บ

วิธีสร้างผลิตภัณฑ์ Blockchain ที่ประสบความสำเร็จ

วิธีการตั้งค่าและเรียกใช้โหนด Ethereumการสัมมนาผ่านเว็บ

วิธีการตั้งค่าและเรียกใช้โหนด Ethereum

วิธีสร้าง Ethereum API ของคุณเองการสัมมนาผ่านเว็บ

วิธีสร้าง Ethereum API ของคุณเอง

วิธีสร้างโซเชียลโทเค็นการสัมมนาผ่านเว็บ

วิธีสร้างโซเชียลโทเค็น

การใช้เครื่องมือรักษาความปลอดภัยในการพัฒนาสัญญาอัจฉริยะการสัมมนาผ่านเว็บ

การใช้เครื่องมือรักษาความปลอดภัยในการพัฒนาสัญญาอัจฉริยะ

อนาคตของการเงินสินทรัพย์ดิจิทัลและ DeFiการสัมมนาผ่านเว็บ

อนาคตของการเงิน: สินทรัพย์ดิจิทัลและ DeFi

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