รายการช่องโหว่ที่พบบ่อย
ช่องโหว่ของสมาร์ตคอนแทร็กต์สามารถนำไปสู่ผลกระทบร้ายแรงและมักจะไม่สามารถย้อนกลับได้เมื่อถูกนำไปใช้งาน การหาประโยชน์จากช่องโหว่เหล่านี้ถือเป็นความท้าทายสำคัญต่อความปลอดภัยของบล็อกเชน ส่งผลให้สินทรัพย์มูลค่าหลายพันล้านดอลลาร์ถูกขโมยและบั่นทอนความเชื่อมั่นของผู้ใช้ ดังนั้น การบรรลุความปลอดภัยที่แข็งแกร่งของสมาร์ตคอนแทร็กต์จึงเป็นสิ่งสำคัญอย่างยิ่ง สิ่งนี้ต้องการมากกว่าแค่การปรับใช้โค้ดบนบล็อกเชน มันต้องการแนวปฏิบัติการเขียนโค้ดที่ปลอดภัยอย่างเข้มงวด การทดสอบอย่างละเอียด และบ่อยครั้งที่ต้องมีการตรวจสอบสมาร์ตคอนแทร็กต์โดยอิสระ เพื่อระบุปัญหาก่อนที่จะถูกนำไปใช้ประโยชน์ในทางที่ผิด การทำความเข้าใจข้อผิดพลาดทั่วไป ตั้งแต่การโจมตีแบบเข้าซ้ำ (reentrancy attacks) เช่น การแฮ็ก DAO ที่ฉาวโฉ่ ไปจนถึงข้อผิดพลาดทางตรรกะที่สังเกตได้ยาก และช่องโหว่ของ Ethereum เช่น โอเวอร์โฟลว์ของจำนวนเต็ม (integer overflows) ถือเป็นก้าวแรกที่สำคัญอย่างยิ่งสู่การลดความเสี่ยง หน้านี้ให้ภาพรวมที่สำคัญเกี่ยวกับช่องโหว่ของสมาร์ตคอนแทร็กต์ที่พบบ่อย เพื่อเพิ่มความตระหนักรู้และส่งเสริมการพัฒนาที่ปลอดภัยยิ่งขึ้นในระบบนิเวศแบบกระจายศูนย์
- Reentrancy
- Unexpected Ether
- DoS
- Overflow
- tx.origin Authentication
- Access Control
- Weak Randomness
- Hash Collision
- Precision Loss
- msg.value in loop
- ecrecover
- Replay
- Inheritance Order
- MEV
ช่องโหว่ Reentrancy เป็นหนึ่งในช่องโหว่ของสัญญาอัจฉริยะ (smart contract) ที่เก่าแก่และสร้างความเสียหายร้ายแรงที่สุด เป็นสาเหตุของการโจมตีครั้งประวัติศาสตร์ที่สำคัญ เช่น การแฮ็ก The DAO ซึ่งนำไปสู่การทำ Hard Fork ของ Ethereum มันยังคงเป็นภัยคุกคามร้ายแรงที่อาจส่งผลกระทบอันน่าสะพรึงกลัว สามารถดูดเงินทั้งหมดออกจากสัญญาได้
ช่องโหว่นี้เกิดจากแอนตี้แพทเทิร์นในการเขียนโค้ดที่พบได้บ่อย ซึ่งมักนำมาจากพฤติกรรมใน Web2 นั่นคือ การดำเนินการโต้ตอบภายนอก (เช่น การโอน Ether หรือการเรียกสัญญาอื่น) ก่อนที่จะอัปเดตสถานะภายในของสัญญา (เช่น ยอดคงเหลือของผู้ใช้) เมื่อสัญญาทำการส่ง Ether หรือเรียกสัญญาภายนอก มันจะโอนการควบคุมการดำเนินการไปชั่วคราว ผู้โจมตีที่ควบคุมสัญญาผู้รับสามารถใช้ประโยชน์จากสิ่งนี้โดยการเรียกกลับมายังสัญญาเดิมทันที ซึ่งมักจะทำผ่านฟังก์ชัน receive หรือ fallback ที่ถูกกระตุ้นโดยการโอน Ether หรือผ่าน hooks (ฮุก) ของมาตรฐานโทเค็น
เนื่องจากสถานะของสัญญาเดิม (เช่น ยอดคงเหลือของผู้โจมตี) ยังไม่ได้รับการอัปเดต การเรียกซ้ำเข้ามา (re-entrant call) จึงผ่านการตรวจสอบเบื้องต้น ทำให้ผู้โจมตีสามารถดำเนินการซ้ำ (เช่น การถอนเงิน) ได้หลายครั้งภายในธุรกรรมเดียวกัน ลูปการเรียกซ้ำนี้จะดำเนินต่อไปจนกว่าเงินทุนจะถูกดูดออกไปจนหมดหรือถึงขีดจำกัดของ gas
แม้ว่าการโจมตีแบบคลาสสิกจะเกี่ยวข้องกับฟังก์ชันเดียว แต่ก็มีรูปแบบที่ซับซ้อนกว่านั้นอยู่ เช่น Reentrancy ข้ามฟังก์ชัน (ใช้ประโยชน์จากสถานะที่ใช้ร่วมกันระหว่างฟังก์ชัน) และ Reentrancy แบบอ่านอย่างเดียว (Read-only reentrancy - ปรับเปลี่ยนการอ่านค่าสถานะผ่าน view functions)
การป้องกันหลักคือรูปแบบ Checks-Effects-Interactions (CEI) หรือ ตรวจสอบ-ผลกระทบ-โต้ตอบ: ทำการตรวจสอบที่จำเป็นทั้งหมด จากนั้นอัปเดตตัวแปรสถานะ (effects - ผลกระทบ) และหลังจากนั้นจึงค่อยโต้ตอบกับสัญญาภายนอก
ช่องโหว่ "การปรับแต่งยอดคงเหลือจากการได้รับ Ether โดยไม่คาดคิด" เกิดขึ้นเนื่องจากสัญญาอัจฉริยะ Solidity สามารถรับ Ether ผ่านกลไกที่ข้ามฟังก์ชัน `receive` และ `fallback` ที่กำหนดไว้ได้ วิธีการหลักสำหรับการฉีด Ether "ที่ไม่คาดคิด" นี้คือ `selfdestruct` opcode ซึ่งบังคับโอนยอดคงเหลือของสัญญาที่กำลังจะถูกทำลายไปยังที่อยู่ที่กำหนด และธุรกรรม coinbase (รางวัลบล็อก)
ประเด็นหลักคือการโอนโดยบังคับเหล่านี้จะเพิ่มยอดคงเหลือ Ether ของสัญญา (`address(this).balance`) โดยตรง โดยไม่กระตุ้นตรรกะที่ตั้งโปรแกรมไว้ของสัญญาสำหรับการจัดการเงินทุนที่เข้ามา ซึ่งเป็นการทำลายข้อสันนิษฐานหรือค่าคงที่ (invariant) ที่พบบ่อย ซึ่งมักเป็นโดยปริยาย: ว่ายอดคงเหลือจริงของสัญญาสะท้อนผลรวมของเงินทุนที่ประมวลผลผ่านฟังก์ชัน `payable` ที่ตั้งใจไว้หรือที่ถูกบันทึกบัญชีภายในอย่างถูกต้อง
สัญญาที่ใช้ `address(this).balance` อย่างไม่ถูกต้องสำหรับการตรวจสอบตรรกะที่สำคัญจะมีความเสี่ยง ตัวอย่างเช่น สัญญาอาจตรวจสอบว่า `address(this).balance == จำนวนเงินที่คาดหวัง` ผู้โจมตีสามารถใช้ประโยชน์จากสิ่งนี้โดยใช้ `selfdestruct` เพื่อส่ง Ether จำนวนเล็กน้อย ซึ่งเป็นการปรับแต่งยอดคงเหลือ สิ่งนี้สามารถนำไปสู่:
การปฏิเสธการให้บริการ (DoS): การตรวจสอบความเท่ากันแบบเข้มงวดอาจเสียหายอย่างถาวร ทำให้ฟังก์ชันใช้งานไม่ได้
การปรับแต่งตรรกะ: ค่าเกณฑ์อาจถึงก่อนกำหนด ทำให้เกิดการเปลี่ยนแปลงสถานะหรือการจ่ายเงินที่ไม่ได้ตั้งใจ
การละเมิด `assert`: ในบางกรณีที่เกิดขึ้นไม่บ่อย ยอดคงเหลือที่ไม่คาดคิดอาจนำไปสู่ความไม่สอดคล้องกันของสถานะภายในซึ่งทำให้ `assert()` ล้มเหลว โดยใช้ gas ของธุรกรรมทั้งหมด
มาตรการป้องกันพื้นฐานคือไม่ควรพึ่งพา `address(this).balance` สำหรับตรรกะของสัญญาเลย แต่สัญญาจะต้องรักษาการบันทึกบัญชีภายในที่แม่นยำโดยใช้ตัวแปรสถานะเฉพาะ โดยอัปเดตเฉพาะภายในฟังก์ชันการจัดการเงินทุนที่ถูกต้องตามกฎหมายเท่านั้น การตรวจสอบที่สำคัญและการเปลี่ยนสถานะทั้งหมดจะต้องขึ้นอยู่กับตัวแปรภายในที่เชื่อถือได้เหล่านี้
ช่องโหว่การโจมตีโดยปฏิเสธการให้บริการ (Denial of Service, DoS) ในสัญญาอัจฉริยะ Solidity (Solidity smart contracts) มีเป้าหมายเพื่อขัดขวางหรือหยุดการทำงานโดยสมบูรณ์ของฟังก์ชันการทำงานที่ตั้งใจไว้ของสัญญา ป้องกันไม่ให้ผู้ใช้ที่ถูกต้องตามกฎหมายเข้าถึงบริการของมัน "บริการ" ที่ถูกปฏิเสธคือความสามารถของสัญญาในการดำเนินการฟังก์ชันต่างๆ ตามที่ได้โปรแกรมไว้และตามที่ผู้ใช้คาดหวัง
ผู้โจมตีบรรลุเป้าหมายนี้โดยการใช้ประโยชน์จากข้อบกพร่องในตรรกะของโค้ดของสัญญา การจัดการกับข้อจำกัดด้านทรัพยากร (โดยหลักคือ gas) หรือการใช้ประโยชน์จากการพึ่งพาภายนอก
รูปแบบที่พบบ่อย ได้แก่:
การทำให้ Gas หมด (Gas Exhaustion): การสร้างธุรกรรมหรือการจัดการกับสถานะ (state) เพื่อทำให้ต้นทุนการดำเนินการฟังก์ชันเกินกว่าขีดจำกัด gas ของบล็อก (block gas limit) หรือขีดจำกัด gas ของธุรกรรม (transaction gas limit) ซึ่งมักเกี่ยวข้องกับการกระตุ้นการดำเนินการที่ใช้การคำนวณสูง หรือที่พบบ่อยมากคือ การวนซ้ำบนอาร์เรย์ที่ไม่จำกัดขนาด (unbounded arrays) ซึ่งขนาดสามารถเพิ่มขึ้นได้อย่างไม่มีที่สิ้นสุด เมื่อข้อมูลมีขนาดใหญ่ขึ้น ต้นทุน gas ของลูปจะเกินขีดจำกัด ทำให้ฟังก์ชันนั้นใช้งานไม่ได้
การย้อนกลับที่ไม่คาดคิด (Unexpected Reverts): การทำให้ฟังก์ชันที่สำคัญย้อนกลับ (revert) อย่างไม่คาดคิด ซึ่งอาจถูกกระตุ้นโดยการบังคับให้การเรียกใช้ภายนอกล้มเหลว (เช่น การส่งเงินไปยังสัญญาที่ปฏิเสธการรับเงิน) การจัดการสถานะเพื่อให้เงื่อนไข `require` ล้มเหลว หรือข้อยกเว้นอื่นๆ ที่ไม่ได้รับการจัดการ หากสัญญาต้องพึ่งพาการเรียกใช้ภายนอกที่ล้มเหลว (ซึ่งอาจถูกชักนำโดยมีเจตนาร้าย) การดำเนินการทั้งหมดอาจถูกบล็อก
ลักษณะที่ไม่สามารถเปลี่ยนแปลงได้ (immutable nature) ของสัญญาอัจฉริยะทำให้ DoS มีความรุนแรงเป็นพิเศษ การโจมตีที่ประสบความสำเร็จอาจนำไปสู่เงินทุนที่ถูกล็อกอย่างถาวรหรือฟังก์ชันการทำงานที่ไม่สามารถกู้คืนได้ เนื่องจากโค้ดของสัญญาไม่สามารถแก้ไขได้ง่ายหลังจากการปรับใช้ (post-deployment)
การบรรเทาผลกระทบอาศัยแนวทางการเขียนโค้ดที่ปลอดภัย: การใช้การดำเนินการที่มีขอบเขตจำกัด (bounded operations) แทนลูปที่ไม่จำกัดขอบเขต (unbounded loops), การเลือกใช้รูปแบบ "pull-payment" (การให้ผู้ใช้ดึงเงินเอง) แทนการ "push-payment" (การส่งเงินให้ผู้ใช้) ซึ่งช่วยแยกความล้มเหลวในการโอนเงิน, การจัดการความล้มเหลวของการเรียกใช้ภายนอกอย่างมีประสิทธิภาพ (เช่น การใช้ `try/catch` ในกรณีที่เหมาะสม) และการจัดการ gas อย่างระมัดระวัง
ช่องโหว่ Integer overflow (จำนวนเต็มล้น) และ underflow (จำนวนเต็มต่ำกว่าค่าต่ำสุด) เป็นภัยคุกคามที่คงอยู่ในขอบเขตความปลอดภัยของสัญญาอัจฉริยะ (smart contract) ซึ่งเกิดจากข้อจำกัดพื้นฐานของการคำนวณเลขจำนวนเต็มขนาดคงที่ Overflow เกิดขึ้นเมื่อผลลัพธ์ของการดำเนินการทางคณิตศาสตร์เกินค่าสูงสุดสำหรับประเภทข้อมูลนั้น และ underflow เกิดขึ้นเมื่อผลลัพธ์ต่ำกว่าค่าต่ำสุด
ในอดีต ใน Solidity เวอร์ชันก่อน 0.8.0 ข้อผิดพลาดทางคณิตศาสตร์เหล่านี้ทำให้ค่า "วนกลับ" (wrap around) อย่างเงียบๆ ตัวอย่างเช่น การบวก 1 เข้ากับค่าสูงสุดของ uint8 (255) จะได้ผลลัพธ์เป็น 0 และการลบ 1 ออกจาก 0 จะได้ผลลัพธ์เป็น 255 พฤติกรรมที่คาดเดาได้แต่ไม่มีการตรวจสอบนี้เป็นแหล่งสำคัญของการใช้ช่องโหว่ (exploit) ทำให้ผู้โจมตีสามารถจัดการยอดคงเหลือโทเค็นได้ (เช่น การสร้างโทเค็นจำนวนมหาศาล หรือการถอนเงินทุนโดยทำให้ยอดคงเหลือวนกลับ) หลีกเลี่ยงการตรวจสอบความปลอดภัยที่สำคัญ ทำให้สถานะของสัญญาเสียหาย หรือทำให้เกิดการปฏิเสธการให้บริการ (denial-of-service)
Solidity เวอร์ชัน 0.8.0 ได้นำเสนอการปรับปรุงความปลอดภัยที่สำคัญ: การดำเนินการทางคณิตศาสตร์มาตรฐาน (+, -, *, /) ในขณะนี้จะทำการตรวจสอบ overflow และ underflow โดยค่าเริ่มต้น หากการดำเนินการใดๆ จะส่งผลให้เกิดการวนกลับ ธุรกรรมจะย้อนกลับ (revert) แทน ซึ่งช่วยป้องกันการทำให้สถานะเสียหายอย่างเงียบๆ
อย่างไรก็ตาม ความเสี่ยงยังไม่ได้ถูกกำจัดไปทั้งหมด นักพัฒนาสามารถหลีกเลี่ยงการตรวจสอบตามค่าเริ่มต้นเหล่านี้ได้อย่างชัดเจนโดยใช้บล็อก `unchecked` ซึ่งมักจะทำเพื่อเพิ่มประสิทธิภาพ gas โค้ดภายในบล็อก `unchecked` จะกลับไปใช้พฤติกรรมการวนกลับแบบเงียบที่เป็นอันตรายของเวอร์ชันก่อน 0.8.0 และต้องการการตรวจสอบที่ระมัดระวังอย่างยิ่ง ในทำนองเดียวกัน โค้ด assembly EVM ระดับต่ำไม่ได้รับประโยชน์จากการตรวจสอบของ Solidity และต้องการการจัดการความปลอดภัยด้วยตนเองสำหรับการดำเนินการทางคณิตศาสตร์ นอกจากนี้ การดำเนินการเลื่อนบิต (shift operations) (<<, >>) ยังคงไม่มีการตรวจสอบแม้ในโหมด >=0.8.0 ตามค่าเริ่มต้น และจะตัดทอนผลลัพธ์ การแปลงชนิดข้อมูล (type casting) ที่ไม่เหมาะสม (เช่น การแปลง uint256 ขนาดใหญ่เป็นชนิดข้อมูลที่เล็กกว่าเช่น uint8) ก็อาจนำไปสู่การตัดทอนค่า ซึ่งอาจทำให้เกิดพฤติกรรมที่ไม่คาดคิดในการคำนวณครั้งต่อไปได้
ดังนั้น แม้ว่าจะได้รับการบรรเทาลงอย่างมากโดยค่าเริ่มต้นใน Solidity สมัยใหม่แล้วก็ตาม integer overflow/underflow ก็ยังคงเป็นช่องโหว่ที่เกี่ยวข้อง โดยเฉพาะอย่างยิ่งในสัญญาเก่า (legacy contracts) โค้ดที่ใช้บล็อก `unchecked` หรือ assembly และผ่านการดำเนินการเฉพาะที่ไม่ได้รับการตรวจสอบ หรือการแปลงชนิดข้อมูลที่ไม่ระมัดระวัง
การใช้ `tx.origin` สำหรับตรรกะการให้สิทธิ์ในสัญญาอัจฉริยะ (smart contracts) ของ Solidity ถือเป็นช่องโหว่ความปลอดภัยที่ร้ายแรง เกิดจากความเข้าใจผิดขั้นพื้นฐานเกี่ยวกับพฤติกรรมของมันเมื่อเทียบกับ `msg.sender` ในขณะที่ `tx.origin` ระบุ EOA (Externally Owned Account / บัญชีที่ควบคุมจากภายนอก) ที่เริ่มต้นธุรกรรมได้อย่างสม่ำเสมอ `msg.sender` จะระบุผู้เรียกใช้ (caller) โดยตรง การใช้ `tx.origin` สำหรับการตรวจสอบสิทธิ์จึงไม่สามารถตรวจสอบเอนทิตี (entity) ที่โต้ตอบกับสัญญาโดยตรงได้
ข้อบกพร่องนี้เปิดช่องให้เกิดการโจมตีแบบฟิชชิง (phishing-style attacks) ซึ่งสัญญากลางที่เป็นอันตรายซึ่งถูกเรียกใช้โดยผู้ใช้ที่มีสิทธิ์ สามารถเรียกใช้ฟังก์ชันที่ได้รับการป้องกันบนสัญญาเป้าหมายได้สำเร็จ การตรวจสอบ `tx.origin` จะตรวจสอบความถูกต้องของผู้ใช้ดั้งเดิมอย่างไม่ถูกต้อง ทำให้สัญญาที่เป็นอันตรายสามารถดำเนินการต่างๆ โดยใช้สิทธิ์ของผู้ใช้ได้ ผลกระทบมีตั้งแต่การขโมย Ether และโทเค็นโดยตรง ไปจนถึงการแก้ไขเปลี่ยนแปลงสถานะของสัญญาโดยไม่ได้รับอนุญาต ซึ่งอาจก่อให้เกิดความสูญเสียทางการเงินอย่างมีนัยสำคัญ และความเสียหายที่ไม่สามารถแก้ไขได้ต่อความสมบูรณ์และชื่อเสียงของโปรโตคอล
การควบคุมการเข้าถึงไม่เพียงพอ (Insufficient Access Control) เป็นช่องโหว่ร้ายแรงในสัญญาอัจฉริยะ (smart contracts) ของ Solidity ซึ่งหมายถึงการที่ข้อจำกัดว่าใครสามารถเรียกใช้งานฟังก์ชันใดได้บ้างนั้นขาดหายไปหรือถูกนำไปใช้อย่างไม่เหมาะสม เนื่องจาก Solidity ไม่มีโมเดลการอนุญาตในตัว นักพัฒนาจึงต้องเพิ่มการตรวจสอบด้วยตนเอง โดยมักใช้ตัวปรับแต่ง (modifiers) เช่น onlyOwner หรือใช้การควบคุมการเข้าถึงตามบทบาท (Role-Based Access Control - RBAC) การไม่ดำเนินการดังกล่าวอย่างถูกต้องจะสร้างช่องโหว่ด้านความปลอดภัย
ช่องโหว่นี้มักแสดงออกมาในรูปแบบของฟังก์ชันที่ไม่ได้รับการป้องกัน ฟังก์ชันที่มีไว้สำหรับการดำเนินการด้านการดูแลระบบหรือข้อมูลที่ละเอียดอ่อน เช่น การโอนความเป็นเจ้าของสัญญา (changeOwner) การถอนเงิน (withdraw) การหยุดสัญญาชั่วคราว การสร้างโทเค็น หรือแม้แต่การทำลายสัญญา (selfdestruct) อาจถูกปล่อยให้บัญชีภายนอกใดๆ ก็สามารถเรียกใช้ได้ สิ่งนี้มักเกิดขึ้นเนื่องจากขาดตัวปรับแต่งการควบคุมการเข้าถึง หรือการมองเห็นฟังก์ชัน (visibility) ที่ไม่ถูกต้อง (เช่น ฟังก์ชันจะเป็น public โดยค่าเริ่มต้นหากไม่ได้ระบุไว้) ฟังก์ชันการเริ่มต้น (initialization functions) ที่เปิดเผย ซึ่งควรทำงานเพียงครั้งเดียว ก็อาจเป็นช่องทางการโจมตีได้หากยังคงสามารถเรียกใช้ได้หลังจากการปรับใช้ (deployment) ซึ่งอาจทำให้ผู้โจมตีสามารถรีเซ็ตความเป็นเจ้าของหรือพารามิเตอร์ที่สำคัญได้
ผลที่ตามมาร้ายแรง ตั้งแต่การที่ผู้ใช้ที่ไม่ได้รับอนุญาตได้รับสิทธิ์ผู้ดูแลระบบ ไปจนถึงการขโมยเงินทุนทั้งหมดที่จัดการโดยสัญญา หรือการทำลายสัญญาอย่างไม่สามารถย้อนกลับได้ การใช้ประโยชน์จากช่องโหว่ในโลกจริง เช่น เหตุการณ์ Parity Wallet และการแฮ็ก LAND Token แสดงให้เห็นถึงศักยภาพในการทำลายล้างของการควบคุมการเข้าถึงที่ไม่เพียงพอ
การบรรเทาความเสี่ยงเกี่ยวข้องกับการใช้การตรวจสอบการควบคุมการเข้าถึงอย่างเข้มงวดกับฟังก์ชันที่ละเอียดอ่อนทั้งหมด การยึดมั่นในหลักการสิทธิ์น้อยที่สุด (Principle of Least Privilege) การใช้รูปแบบที่ยอมรับกันโดยทั่วไปเช่น Ownable หรือ RBAC (มักผ่านไลบรารีเช่น OpenZeppelin) และการดำเนินการทดสอบและตรวจสอบอย่างละเอียด
การสร้างค่าสุ่มที่ปลอดภัยบน Ethereum Virtual Machine (EVM) เป็นเรื่องท้าทายเนื่องจากลักษณะที่เป็นแบบกำหนด (deterministic nature) ซึ่งจำเป็นสำหรับฉันทามติของเครือข่าย สิ่งนี้สร้าง "ภาพลวงตาของเอนโทรปี" (entropy illusion) ซึ่งค่าที่ดูเหมือนสุ่มที่ได้มาจากข้อมูลบนเชนล้วนๆ นั้น จริงๆ แล้วสามารถคาดเดาได้
นักพัฒนามักใช้ตัวแปรบล็อกที่หาได้ง่าย เช่น block.timestamp, blockhash และ prevrandao หลังการ Merge (เข้าถึงผ่าน block.difficulty) เป็นแหล่งที่มาของค่าสุ่มเทียม (pseudo-randomness) ตัวแปรเหล่านี้ไม่ปลอดภัยเนื่องจากสามารถคาดการณ์หรือมีอิทธิพลต่อค่าได้
นักขุด (ใน Proof-of-Work) หรือผู้ตรวจสอบความถูกต้อง (ใน Proof-of-Stake) สามารถจัดการค่าเหล่านี้ได้ในระดับหนึ่งเพื่อสร้างความได้เปรียบอย่างไม่เป็นธรรม ซึ่งมักเป็นส่วนหนึ่งของกลยุทธ์ MEV (Maximum Extractable Value) ที่สำคัญคือ แม้แต่ผู้ใช้ทั่วไปหรือสัญญาของฝ่ายโจมตีก็มักจะคาดการณ์ผลลัพธ์ได้ หากตรรกะการสุ่มนั้นอาศัยเพียงข้อมูลที่ทราบก่อนหรือระหว่างการดำเนินการธุรกรรม ซึ่งทำให้เกิดการโจมตีแบบ front-running ได้ การคาดการณ์ได้นี้บ่อนทำลายความเป็นธรรมของแอปพลิเคชัน เช่น ลอตเตอรี่ เกม และการสร้าง NFT
การชนกันของแฮช (Hash Collision) ผ่าน `abi.encodePacked` ที่มีประเภทข้อมูลแบบไดนามิกหลายประเภท ไม่ได้เกิดจากจุดอ่อนในฟังก์ชันแฮช Keccak-256 ที่อยู่เบื้องหลัง แต่เกิดจากวิธีการเข้ารหัสข้อมูลก่อนที่จะทำการแฮช โดยเฉพาะเมื่อใช้ฟังก์ชัน `abi.encodePacked` ของ Solidity ซึ่งแตกต่างจาก `abi.encode` มาตรฐาน ที่จะเติมข้อมูล (padding) ให้กับอาร์กิวเมนต์จนครบ 32 ไบต์ และรวมถึงคำนำหน้าความยาว (length prefix) สำหรับประเภทข้อมูลแบบไดนามิก แต่ `abi.encodePacked` จะสร้างการเข้ารหัสที่กระชับและไม่เป็นมาตรฐาน โดยการต่ออาร์กิวเมนต์เข้าด้วยกันโดยใช้จำนวนไบต์ที่น้อยที่สุดที่จำเป็น ละเว้นการเติมข้อมูลสำหรับประเภทข้อมูลสแตติกขนาดเล็ก และที่สำคัญคือ ละเว้นข้อมูลความยาวสำหรับประเภทข้อมูลแบบไดนามิก เช่น `string`, `bytes` หรืออาร์เรย์ไดนามิก
ปัญหาหลักเกิดขึ้นเมื่อใช้ `abi.encodePacked` กับอาร์กิวเมนต์ประเภทไดนามิกที่อยู่ติดกันตั้งแต่สองตัวขึ้นไป เนื่องจากความยาวของอาร์กิวเมนต์ไดนามิกแต่ละตัวไม่ได้ถูกเข้ารหัส ขอบเขตระหว่างอาร์กิวเมนต์เหล่านั้นจึงกลายเป็นความกำกวมในสตริงไบต์ผลลัพธ์ ความกำกวมนี้ทำให้สามารถสร้างชุดข้อมูลอินพุตเชิงตรรกะที่แตกต่างกัน (ซึ่งมักทำได้ง่าย) เพื่อสร้างลำดับไบต์ที่ถูกแพ็กแบบเดียวกันได้ ตัวอย่างเช่น `abi.encodePacked("a", "bc")` ให้ผลลัพธ์ไบต์เหมือนกับ `abi.encodePacked("ab", "c")`
เมื่อผลลัพธ์ไบต์ที่เหมือนกันนี้ถูกนำไปแฮชในภายหลัง (เช่น `keccak256(abi.encodePacked(...))`) จะส่งผลให้ได้ค่าแฮชเดียวกัน ซึ่งเป็นการชนกันของแฮชที่เกิดจากการเข้ารหัส
ช่องโหว่การชนกันของการเข้ารหัสนี้สามารถถูกใช้ประโยชน์ได้หลายวิธี หากแฮชผลลัพธ์ถูกนำไปใช้ในบริบทที่ละเอียดอ่อนด้านความปลอดภัย:
การหลีกเลี่ยงการตรวจสอบลายเซ็น (Signature Verification Bypass): ผู้โจมตีสามารถนำลายเซ็นที่ถูกต้องซึ่งสร้างขึ้นสำหรับชุดพารามิเตอร์หนึ่งชุด มาใช้ซ้ำกับชุดพารามิเตอร์ที่แตกต่างกันและเป็นอันตราย ซึ่งสร้างแฮชที่ชนกัน การตรวจสอบลายเซ็นของสัญญา (`ecrecover`) จะสำเร็จ ทำให้สามารถดำเนินการที่ไม่ได้รับอนุญาตได้
การทำให้สถานะเสียหายผ่านการชนกันของคีย์ Mapping (State Corruption via Mapping Key Collisions): หากแฮชที่มีแนวโน้มจะชนกันถูกใช้เป็นคีย์ใน mapping (`mapping(bytes32 => ...)`) ผู้โจมตีสามารถสร้างอินพุตเพื่อสร้างคีย์ที่ชนกับคีย์ของผู้ใช้ที่ถูกต้อง ซึ่งอาจเขียนทับข้อมูลของผู้ใช้ หลีกเลี่ยงการควบคุมการเข้าถึง หรือทำให้เกิดการปฏิเสธบริการ (Denial of Service)
ปัญหาการรับรองความถูกต้องของข้อความ (Message Authentication Issues): ช่องโหว่นี้บ่อนทำลายการตรวจสอบที่อาศัยแฮชเพื่อรับรองความสมบูรณ์ของข้อมูล เนื่องจากข้อความเชิงตรรกะที่แตกต่างกันอาจดูเหมือนกันหลังจากการแฮช
ผลที่ตามมาของการใช้ประโยชน์ที่ประสบความสำเร็จอาจรุนแรง รวมถึง การเข้าถึงฟังก์ชันหรือข้อมูลโดยไม่ได้รับอนุญาต (Unauthorized Access), การขโมยเงินทุนโดยตรง (Fund Theft), การทำให้สถานะเสียหายอย่างร้ายแรง (Critical State Corruption) และการปฏิเสธบริการ (Denial of Service - DoS)
ช่องโหว่การสูญเสียความแม่นยำเกิดจากการพึ่งพาการคำนวณเลขจำนวนเต็ม และการขาดการรองรับจำนวนทศนิยม (floating-point) แบบเนทีฟ การออกแบบนี้ให้ความสำคัญกับการดำเนินการที่กำหนดผลลัพธ์แน่นอน แต่ต้องการให้นักพัฒนาจัดการค่าเศษส่วนด้วยตนเอง ซึ่งสร้างโอกาสสำหรับข้อผิดพลาด
ปัญหาหลักคือการตัดทอนการหารเลขจำนวนเต็ม: Solidity จะทิ้งเศษและปัดเศษผลหารเข้าใกล้ศูนย์ พฤติกรรมที่คาดเดาได้นี้สามารถถูกใช้ประโยชน์ได้ โดยมักผ่านรูปแบบเช่น:
การหารก่อนการคูณ: การคำนวณ (a / b) * c แทนที่จะเป็น (a * c) / b จะตัดทอนผลลัพธ์ระหว่างกลาง a / b ทำให้การสูญเสียความแม่นยำเพิ่มขึ้น
การปัดเศษลงเป็นศูนย์: หากตัวตั้ง A น้อยกว่าตัวหาร B (และทั้งคู่เป็นบวก) ผลลัพธ์ A / B จะเป็น 0 เสมอ สิ่งนี้มีความเสี่ยงสำหรับการคำนวณที่เกี่ยวข้องกับค่าธรรมเนียม รางวัล หรือการแปลงโทเค็นจำนวนน้อย
ผู้โจมตีใช้ประโยชน์จากคุณสมบัติทางคณิตศาสตร์เหล่านี้เพื่อบิดเบือนตรรกะของสัญญาเพื่อผลประโยชน์ทางการเงิน กลยุทธ์ทั่วไป ได้แก่:
การบิดเบือนสถานะ/ราคา: การทำให้เกิดข้อผิดพลาดในการปัดเศษเพื่อบิดเบือนค่าโปรโตคอลที่สำคัญ เช่น อัตราแลกเปลี่ยน, เงินสำรองในพูล, ราคาหุ้นของ vault หรืออัตราส่วนหลักประกัน ซึ่งสามารถนำไปใช้ประโยชน์ในการทำธุรกรรมครั้งต่อไปได้
การกำหนดเป้าหมายไปที่กรณีสุดขอบ (Edge Cases): การใช้ธุรกรรมที่มีอินพุตขนาดเล็กมาก หรืออินพุตที่ออกแบบมาเพื่อโต้ตอบกับค่าภายในขนาดใหญ่ เพื่อเพิ่มผลกระทบของการตัดทอนให้สูงสุด ซึ่งมักจะทำให้การคำนวณได้ผลลัพธ์เป็นศูนย์
การโจมตีด้วยการสูญเสียความแม่นยำที่ประสบความสำเร็จสามารถนำไปสู่ผลกระทบเชิงลบที่สำคัญ:
ต้นทุนที่ลดลง: ผู้โจมตีจ่ายค่าธรรมเนียม/ต้นทุนที่ต่ำลงหรือเป็นศูนย์
ผลกำไรที่สูงเกินจริง: ผู้โจมตีได้รับโทเค็น หุ้น หรือรางวัลมากขึ้นอย่างผิดกฎหมาย
โอกาสในการเก็งกำไร (Arbitrage): การสร้างความแตกต่างของราคาเทียมภายในโปรโตคอลเพื่อให้ผู้โจมตีใช้ประโยชน์
การหลีกเลี่ยงกลไกความเสี่ยง: การหลีกเลี่ยงการชำระบัญชี (liquidations) หรือการตรวจสอบความปลอดภัยอื่น ๆ เนื่องจากการคำนวณที่ไม่ถูกต้อง
การลดลงของเงินทุนอย่างค่อยเป็นค่อยไป: การดูดมูลค่าผ่านธุรกรรมซ้ำ ๆ ที่ใช้ประโยชน์จากข้อผิดพลาดในการปัดเศษเล็กน้อย ("การโจมตี 1 wei")
การบรรเทาผลกระทบเกี่ยวข้องกับการจัดการการคำนวณอย่างระมัดระวัง เช่น การคูณก่อนการหาร, การใช้การปรับสเกลตัวเลข (จำลองการคำนวณแบบจุดตายตัว), การใช้ไลบรารีคณิตศาสตร์เฉพาะทาง และการใช้ตรรกะการปัดเศษที่เหมาะสม การตรวจสอบ overflow มาตรฐาน (เช่น SafeMath หรือ Solidity >=0.8) ไม่สามารถป้องกันการสูญเสียความแม่นยำจากการหารได้
ช่องโหว่นี้เกิดขึ้นเมื่อสัญญาอัจฉริยะ (smart contract) ใช้ msg.value อย่างไม่เหมาะสมภายในลูป (loop) ปัญหาหลักเกิดจากข้อเท็จจริงที่ว่า msg.value จะคงที่ตลอดบริบทการดำเนินการทั้งหมดของธุรกรรม (transaction) หากลูปทำงานซ้ำหลายครั้ง โดยทำการตรวจสอบหรือดำเนินการตามค่า msg.value เริ่มต้นนี้ในแต่ละรอบ โดยไม่ได้ติดตามมูลค่าสะสมที่ประมวลผลหรือใช้ไปในรอบเหล่านั้นอย่างถูกต้อง จะทำให้เกิดช่องทางการใช้ประโยชน์ (exploit)
ผู้โจมตีสามารถใช้ประโยชน์จากสิ่งนี้ได้โดยการส่ง Ether จำนวนหนึ่งเพื่อเรียกใช้ฟังก์ชันที่มีช่องโหว่ ภายในลูป การตรวจสอบเช่น require(msg.value >= amount_per_item) อาจผ่านได้ซ้ำๆ หรือการอัปเดตสถานะอาจใช้ค่า msg.value เริ่มต้นเต็มจำนวนอย่างไม่ถูกต้องหลายครั้ง สิ่งนี้เกิดขึ้นเนื่องจากตรรกะของสัญญาไม่สามารถคำนวณมูลค่าที่ 'ใช้ไป' หรือจัดสรรอย่างมีประสิทธิภาพในรอบก่อนหน้าของลูปเดียวกันได้
ข้อบกพร่องนี้ช่วยให้ผู้โจมตีสามารถเรียกให้เกิดการดำเนินการ (เช่น การโอน Ether หรือการให้เครดิตยอดคงเหลือภายใน) ซึ่งมีมูลค่ารวมเกินกว่า Ether ที่พวกเขาส่งมาพร้อมกับธุรกรรมอย่างมาก
ecrecover เป็นพรีคอมไพล์ EVM ที่จำเป็น ซึ่งช่วยให้สัญญาสมาร์ทคอนแทร็กต์สามารถกู้คืนที่อยู่ของผู้ลงนามได้จากแฮชของข้อความและลายเซ็น ECDSA (v, r, s) ทำให้สามารถใช้งานฟังก์ชันสำคัญๆ ได้ เช่น การตรวจสอบข้อความที่ลงนามนอกเชน (off-chain) สำหรับ meta-transactions หรือฟังก์ชัน permit อย่างไรก็ตาม การใช้งานโดยตรงมีความเสี่ยงด้านความปลอดภัยที่สำคัญและมักถูกประเมินต่ำเกินไปหากไม่จัดการอย่างระมัดระวัง
ช่องโหว่การคืนค่าที่อยู่ศูนย์ (Zero Address Return): ปัญหาสำคัญเกิดจากการจัดการข้อผิดพลาดที่เป็นเอกลักษณ์ของ `ecrecover` เมื่อได้รับลายเซ็นที่ไม่ถูกต้องหรือไม่สามารถเกิดขึ้นได้ทางคณิตศาสตร์ มันจะไม่ย้อนกลับ (revert) ธุรกรรม แต่จะล้มเหลวอย่างเงียบๆ และคืนค่าที่อยู่ศูนย์ (`address(0)`) แทน สัญญาที่เรียกใช้ `ecrecover` แต่ขาดการตรวจสอบที่อยู่ศูนย์มีความเสี่ยงสูงมาก ผู้โจมตีสามารถส่งข้อมูลลายเซ็นที่ไม่ถูกต้องโดยเจตนาได้ ทำให้ `ecrecover` คืนค่า `address(0)` หากผลลัพธ์นี้ไม่ได้รับการตรวจสอบและปฏิเสธอย่างชัดเจน สัญญาอาจดำเนินการต่ออย่างไม่ถูกต้อง โดยถือว่า `address(0)` เป็นผู้ลงนามที่ถูกต้อง ซึ่งอาจนำไปสู่ผลกระทบร้ายแรง เช่น การเปลี่ยนแปลงสถานะโดยไม่ได้รับอนุญาต การปล่อยอีเวนต์ที่ไม่ถูกต้อง หรือการให้สิทธิ์ โดยเฉพาะอย่างยิ่งหากที่อยู่ศูนย์มีสิทธิพิเศษหรือสถานะที่มีความหมายภายในตรรกะเฉพาะของสัญญา โค้ดที่แข็งแกร่งต้องตรวจสอบ `recoveredAddress != address(0)` เสมอทันทีหลังจากเรียกใช้ `ecrecover`
ช่องโหว่ความอ่อนตัวของลายเซ็น (Signature Malleability): ความเสี่ยงหลักประการที่สองเกิดขึ้นจากคุณสมบัติโดยธรรมชาติของอัลกอริทึม ECDSA เอง นั่นคือความอ่อนตัวของลายเซ็น (signature malleability) สำหรับข้อความและคีย์ส่วนตัวใดๆ อาจมีการแสดงลายเซ็นที่แตกต่างกันแต่ถูกต้องตามหลักการเข้ารหัสลับหลายรูปแบบ (โดยเฉพาะอย่างยิ่ง ลายเซ็นที่ใช้ส่วนประกอบ `s` มักจะสามารถแปลงเป็นลายเซ็นที่ถูกต้องโดยใช้ `n-s` ได้ โดยที่ `n` คือลำดับของเส้นโค้ง) สิ่งนี้จะกลายเป็นช่องโหว่หากสัญญาเข้าใจผิดว่าลายเซ็นสำหรับข้อความนั้นมีเพียงหนึ่งเดียว ผู้โจมตีสามารถใช้ประโยชน์จากสิ่งนี้โดยการหลีกเลี่ยงการตรวจสอบความเป็นเอกลักษณ์ ตัวอย่างเช่น หากสัญญาใช้แฮชของลายเซ็นเป็น nonce เพื่อป้องกันการโจมตีซ้ำ (replay attacks) (ซึ่งเป็นรูปแบบที่บกพร่อง) ผู้โจมตีสามารถนำลายเซ็นที่ถูกต้องมาคำนวณรูปแบบที่อ่อนตัวได้ แล้วส่งเพื่อดำเนินการอีกครั้ง เนื่องจากแฮชของลายเซ็นจะแตกต่างกัน นอกจากนี้ยังอาจทำให้เกิดพฤติกรรมที่ไม่คาดคิดหรือการโจมตีซ้ำในระบบที่คาดหวังรูปแบบลายเซ็นเฉพาะ หากระบบภายนอกหรือส่วนต่างๆ ของตรรกะสัญญาไม่ได้ออกแบบมาเพื่อรองรับรูปแบบลายเซ็นที่ถูกต้องทั้งสองรูปแบบ การบรรเทาผลกระทบที่มีประสิทธิภาพคือการบังคับใช้การทำให้ลายเซ็นเป็นมาตรฐาน (signature canonicalization) – ซึ่งเป็นการตรวจสอบที่นำมาใช้อย่างแข็งแกร่งในไลบรารีมาตรฐาน เช่น ECDSA ของ OpenZeppelin ซึ่งควรเลือกใช้มากกว่าการใช้ `ecrecover` โดยตรง
การโจมตีแบบรีเพลย์ข้ามเชน (Cross-Chain Replay Attack - CCRA): ธุรกรรมที่ดำเนินการอย่างถูกต้องบนเชน EVM หนึ่ง ถูกดักจับและส่งซ้ำได้สำเร็จบนเชน EVM อื่น การโจมตีนี้ใช้ประโยชน์จากความคล้ายคลึงกันในรูปแบบธุรกรรมและลายเซ็นดิจิทัลข้ามเชน โดยเฉพาะอย่างยิ่งเมื่อธุรกรรมขาดตัวระบุเชนที่ไม่ซ้ำกัน (Chain ID) การทำ Hard Fork ของ Ethereum/Ethereum Classic เป็นตัวอย่างคลาสสิกที่ความเสี่ยงนี้เกิดขึ้นจริง EIP-155 ถูกนำมาใช้เพื่อบรรเทาปัญหานี้โดยการฝัง Chain ID ลงในลายเซ็นธุรกรรมมาตรฐาน ทำให้ลายเซ็นนั้นๆ เฉพาะเจาะจงกับเชนนั้นๆ อย่างไรก็ตาม สัญญาอัจฉริยะ (Smart Contract) ที่ใช้การตรวจสอบลายเซ็นแบบกำหนดเองก็ต้องตรวจสอบ Chain ID อย่างชัดเจนด้วย การเจาะระบบ (exploit) มูลค่า 20 ล้านดอลลาร์บน Optimism ที่กระทำต่อ Wintermute เป็นผลมาจากการขาดการตรวจสอบ Chain ID ดังกล่าวในสัญญาที่ปรับใช้ข้ามเชน (cross-chain)
การรีเพลย์ระดับสัญญาอัจฉริยะ (เชนเดียวกัน): ข้อความหรือธุรกรรมที่ลงนามแล้ว ถูกนำมาใช้ซ้ำ (replayed) กับสัญญาอัจฉริยะเดิม หรืออาจเป็นสัญญากับสัญญาอื่นบนเชนเดียวกัน โดยทั่วไปแล้ว สิ่งนี้จะใช้ประโยชน์จากช่องโหว่ภายในตรรกะของสัญญาเอง โดยเฉพาะอย่างยิ่งในรูปแบบการตรวจสอบลายเซ็นแบบกำหนดเองที่ใช้สำหรับฟีเจอร์ต่างๆ เช่น เมตาทรานแซคชัน (meta-transactions) หรือฟังก์ชันการอนุญาต (permit) ของ ERC-20 ข้อบกพร่องที่พบบ่อยที่สุดคือการไม่มีอยู่หรือการใช้งาน Nonce ระดับแอปพลิเคชันที่ไม่เหมาะสม Nonce («ตัวเลขที่ใช้เพียงครั้งเดียว») คือตัวนับที่ไม่ซ้ำกันซึ่งเชื่อมโยงกับผู้ลงนาม ซึ่งต้องรวมอยู่ในข้อมูลสรุป (digest) ของข้อความที่ลงนามแล้ว และถูกติดตามบนเชน (on-chain) โดยสัญญา เพื่อให้แน่ใจว่าลายเซ็นเฉพาะแต่ละรายการจะอนุญาตให้ดำเนินการได้เพียงครั้งเดียวเท่านั้น
การรองรับการสืบทอดหลายสายของ Solidity ช่วยให้คอนแทร็กต์สามารถสืบทอดคุณสมบัติจากคอนแทร็กต์แม่หลายตัวพร้อมกันได้ แม้ว่าจะมีประสิทธิภาพในการนำโค้ดกลับมาใช้ใหม่ แต่ก็ก่อให้เกิดความกำกวมที่อาจเกิดขึ้นได้ ซึ่งเรียกว่า "ปัญหาเพชร" (Diamond Problem): หากคอนแทร็กต์พื้นฐานตั้งแต่สองตัวขึ้นไปกำหนดฟังก์ชันที่มีชื่อและพารามิเตอร์เดียวกัน
เพื่อแก้ไขปัญหานี้ Solidity ใช้อัลกอริทึม C3 linearization เพื่อสร้างลำดับการหาเมธอด (Method Resolution Order, MRO) ที่แน่นอนและเป็นหนึ่งเดียวสำหรับแต่ละคอนแทร็กต์ MRO นี้จะกำหนดลำดับที่แม่นยำในการตรวจสอบคอนแทร็กต์พื้นฐานเมื่อทำการแก้ไขการเรียกใช้ฟังก์ชัน
ช่องโหว่เกิดขึ้นโดยตรงจากวิธีการกำหนด MRO นี้ ปัจจัยสำคัญคือลำดับที่นักพัฒนาแสดงรายการคอนแทร็กต์พื้นฐานในคำสั่ง `is` Solidity กำหนดให้แสดงรายการคอนแทร็กต์จาก "เหมือนพื้นฐานมากที่สุด" ไปจนถึง "สืบทอดมากที่สุด" ลำดับที่ระบุนี้มีอิทธิพลโดยตรงต่อ MRO สุดท้ายที่สร้างโดยอัลกอริทึม C3
ช่องโหว่เกิดขึ้นเมื่อนักพัฒนาให้ลำดับการสืบทอดที่ไม่ตรงกับลำดับชั้นเชิงตรรกะหรือลำดับความสำคัญที่ตั้งใจไว้ หากคอนแทร็กต์ทั่วไปถูกระบุไว้หลังคอนแทร็กต์ที่เฉพาะเจาะจงกว่า หรือลำดับนั้นทำให้ MRO จัดลำดับความสำคัญของการใช้งานฟังก์ชันที่ไม่ได้ตั้งใจ คอนแทร็กต์อาจทำงานผิดปกติได้ ตัวอย่างเช่น การเรียกอาจแก้ไขไปยังฟังก์ชันพื้นฐานที่ขาดการตรวจสอบความปลอดภัยที่สำคัญหรือตรรกะที่อัปเดตซึ่งนำไปใช้ในคอนแทร็กต์ที่สืบทอดที่ตั้งใจไว้แต่เรียงลำดับไม่ถูกต้อง
ผลที่ตามมาของการเรียกใช้ฟังก์ชันที่ไม่ถูกต้องเนื่องจากลำดับการสืบทอดที่ไม่ถูกต้อง ได้แก่ การข้ามการควบคุมการเข้าถึง การเรียกใช้ตรรกะทางธุรกิจที่ล้าสมัยหรือไม่ถูกต้อง การเสียหายของสถานะ และการสูญเสียทางการเงินที่อาจเกิดขึ้น โดยพื้นฐานแล้ว โฟลว์การทำงานจริงของคอนแทร็กต์เบี่ยงเบนไปจากการออกแบบของนักพัฒนา ซึ่งบั่นทอนความปลอดภัยและฟังก์ชันการทำงาน
มูลค่าสูงสุดที่สกัดได้ (Maximal Extractable Value - MEV) หมายถึงผลกำไรที่ผู้ผลิตบล็อกและผู้ค้นหาเฉพาะทาง (searchers) สามารถได้รับจากการควบคุมการรวมและการจัดลำดับธุรกรรมภายในบล็อก ซึ่งนอกเหนือไปจากรางวัลบล็อกมาตรฐานและค่าธรรมเนียม Gas เดิมทีเรียกว่า "มูลค่าที่นักขุดสกัดได้" (Miner Extractable Value) แต่ต่อมาชื่อได้เปลี่ยนเป็น "สูงสุด" (Maximal) เพื่อสะท้อนถึงความต้องการผลตอบแทนที่มากขึ้น
MEV เกิดขึ้นเนื่องจากธุรกรรมที่รอดำเนินการมักจะอยู่ในพื้นที่รอยืนยันสาธารณะที่เรียกว่า เมมพูล (mempool) ซึ่งทุกคนสามารถมองเห็นได้ ผู้ผลิตบล็อกมีอำนาจในการตัดสินใจลำดับสุดท้ายของธุรกรรมในบล็อก บอทอัตโนมัติที่ดำเนินการโดย "ผู้ค้นหา" (searchers) จะคอยตรวจสอบเมมพูลอยู่ตลอดเวลา จำลองผลลัพธ์ที่เป็นไปได้ และใช้ประโยชน์จากโอกาสในการทำกำไรโดยการจัดลำดับธุรกรรมอย่างมีกลยุทธ์ ซึ่งมักจะใช้การประมูล Gas (Priority Gas Auctions - PGA) เพื่อให้แน่ใจว่าจะได้รับการจัดลำดับตามที่ต้องการ
กลยุทธ์ MEV ทั่วไป ซึ่งมักถูกมองว่าเป็นการโจมตี ได้แก่:
การวิ่งตัดหน้า (Front-Running): การวางธุรกรรมของผู้โจมตีก่อนหน้าธุรกรรมของเหยื่อ (เช่น การซื้อขายขนาดใหญ่บน DEX) เพื่อทำกำไรจากผลกระทบต่อราคาที่คาดการณ์ไว้
การโจมตีแบบแซนด์วิช (Sandwich Attacks): การผสมผสานระหว่างการวิ่งตัดหน้า (front-running) และการวิ่งตามหลัง (back-running) ธุรกรรม DEX ของเหยื่อ เพื่อเก็บส่วนต่างราคา (slippage) ที่เกิดจากการควบคุม
สภาพคล่องแบบทันเวลาพอดี (JIT Liquidity): การเพิ่มและถอนสภาพคล่องชั่วคราวรอบ ๆ การแลกเปลี่ยน (swap) ขนาดใหญ่บน DEX ที่มีสภาพคล่องแบบเข้มข้น เพื่อเก็บค่าธรรมเนียม
การปั่นราคา Oracle (Oracle Manipulation): การใช้ประโยชน์จากการอัปเดตหรือความไม่ถูกต้องของ Oracle ราคาเพื่อทำกำไร ซึ่งมักส่งผลกระทบต่อโปรโตคอลการให้กู้ยืม
ประเภทอื่น ๆ ได้แก่ การดักซื้อ NFT (NFT sniping) และการโจมตีด้วยเศษธุรกรรม (dust attacks)
ผลกระทบต่อผู้ใช้ ได้แก่ ต้นทุนธุรกรรมที่เพิ่มขึ้นเนื่องจากสงคราม Gas, ราคาดำเนินการที่แย่ลง (slippage) ในการซื้อขาย, การขาดทุนที่ไม่ถาวร (impermanent loss) ที่รุนแรงขึ้นสำหรับผู้ให้บริการสภาพคล่อง และการบังคับขาย (liquidations) ที่เกิดขึ้นอย่างไม่เป็นธรรม
กลยุทธ์การบรรเทาผลกระทบมีเป้าหมายเพื่อลดผลกระทบเชิงลบของ MEV การส่งธุรกรรมผ่านเมมพูลส่วนตัวหรือรีเลย์ (เช่น Flashbots Protect หรือ MEV Blocker) จะช่วยซ่อนธุรกรรมจากการมองเห็นของสาธารณะ การออกแบบระดับแอปพลิเคชัน เช่น รูปแบบ Commit-Reveal (ยืนยัน-เปิดเผย) จะบดบังรายละเอียดธุรกรรมจนกว่าการจัดลำดับจะเสร็จสิ้น ในขณะที่การใช้ราคาเฉลี่ยถ่วงน้ำหนักตามเวลา (TWAP) สำหรับ Oracle สามารถลดความเสี่ยงในการถูกควบคุมได้