วิธีป้องกันการโจมตี Input Validation ใน Solidity

ผู้เขียนบทความ

ตามข้อมูลที่ได้รับ การโจมตีแบบ Missing input validation จะเกิดขึ้นเมื่อสัญญา Solidity รับค่าจากผู้ใช้ซึ่งควบคุมโดยฝ่ายภายนอก โดยไม่ได้พิสูจน์ว่าค่านั้นตรงตามสมมุติฐานด้านเศรษฐกิจและความปลอดภัยของสัญญา เช่น การรับที่อยู่จำนวนเงิน รหัสหรือค่ากำหนดต่าง ๆ โดยไม่ได้ตรวจสอบให้แน่ใจว่าค่าเหล่านั้นมีความถูกต้องและปลอดภัยก่อนดำเนินการ เช่น การเคลื่อนย้ายสินทรัพย์ หรือการบันทึกบัญชี

ผลกระทบที่เกิดขึ้นเป็นรูปธรรมอย่างชัดเจน: คิวบิตสูญเสียเงินกว่า 80.0 ล้านดอลลาร์บน BSC เมื่อวันที่ 28 มกราคม 2022 จากเหตุการณ์ที่จัดอยู่ในกลุ่ม Missing Input Validation ความล้มเหลวเหล่านี้ไม่ใช่ปัญหาเพียง Transaction ที่ผิดรูปแบบเท่านั้น แต่เป็นความล้มเหลวด้านการตรวจสอบค่าที่ไม่ได้รับการตรวจสอบอย่างเหมาะสม ทำให้โค้ด Solidity ที่ถูกต้องตามไวยากรณ์สามารถดำเนินการตามธุรกรรมที่ผิดกฎหมาย เช่น การเครดิตทรัพย์สินที่ไม่มีอยู่จริง การโอนสินทรัพย์ไปยังปลายทางที่ไม่น่าเชื่อถือ หรือการสร้างหนี้สินเกินขีดจำกัดที่แท้จริงของผู้ขอกู้

อะไรคือจุดอ่อนด้านการ validate inputs ใน Solidity และการโจมตีทำงานอย่างไร?

จุดอ่อนด้านการ Validate input ใน Solidity เกิดขึ้นเมื่อสัญญา Treat ค่าที่ส่งจากผู้ใช้อย่างน่าเชื่อถือทั้งที่จริง ๆ แล้ว ค่านั้นควบคุมสิทธิ์ การบันทึกทรัพย์สิน ราคา หรือสถานะการเปลี่ยนแปลงต่าง ๆ ของระบบ แต่นักโจมตีสามารถส่งค่าที่ถูกต้องตามไวยากรณ์สำหรับ ABI แต่ผิดด้านเศรษฐกิจตาม protocol และใช้สถานะที่เกิดขึ้นในการถอนทรัพย์สิน การสร้างสิทธิ์ หรือข้ามข้อจำกัดได้

Solidity จะเข้มงวดกับชนิดข้อมูล ไม่ใช่เจตนารมณ์ของโปรโตคอล เช่น uint256 สามารถเป็นตัวเลขใดก็ได้ที่ไม่ทำให้เกิด revert และ address อาจเป็น EOA สัญญาอันตราย Address ที่เป็น zero หรือโทเคนที่ไม่รองรับ สัญญาต้องบังคับใช้กฎด้านความหมายที่ขาดหายไปนี้

ลำดับการโจมตีแบบทั่วไปคือ:

  1. นักโจมตีระบุฟังก์ชันสาธารณะที่รับจำนวนเงิน ทรัพย์สิน ผู้รับ ราคา หรือรหัสที่ไม่ได้ตรวจสอบ
  2. นักโจมตีเลือกค่าที่ละเมิดสมมุติฐานของโปรโตคอล
  3. สัญญาเก็บยอดเงิน สถานะสิทธิ์ ขีดจำกัดหนี้ หรือสิทธิ์การอนุญาตที่ไม่ปลอดภัย
  4. นักโจมตีเรียกใช้ฟังก์ชันในภายหลังที่เชื่อถือสถานะที่เป็นพิษนั้น
  5. โปรโตคอลโอนทรัพย์สินหรือสร้างสิทธิ์ที่สามารถโอนได้บนสถานะเท็จนั้น
  6. ผู้ใช้อื่นเป็นฝ่ายรับความสูญเสียเมื่อต้นทุนบัญชีเริ่มเปิดเผย

คำถามสำคัญคือ: ข้อเท็จจริงที่ input นี้อ้างถึงคืออะไร และสัญญายืนยันความถูกต้องของข้อเท็จจริงนั้นอย่างอิสระที่ไหน? ถ้าคำตอบคือ “ผู้เรียกส่งมาเอง” สัญญานั้นมีจุดอ่อนด้านการ validate input อย่างไม่เหมาะสม

รูปแบบของ Solidity ที่ก่อให้เกิดการโจมตีแบบ validate input

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

  • Collateral value ที่ประกาศโดย caller: ผู้กู้ให้ค่าที่ใช้คำนวณอำนาจในการกู้ยืม
  • Address ของโทเคนแบบสุ่ม: ฟังก์ชันฝากเงินรับ ERC-20 ใดก็ได้ รวมถึงโทเคนที่มีพฤติกรรมการโอนที่ไม่เป็นไปตามมาตรฐาน
  • Address ของผู้รับที่ไม่ได้ตรวจสอบ: รับ address(0), ตัวสัญญาเอง หรือ address ที่ไม่ถูกต้องตามกฎการดูแลทรัพย์สิน
  • เปอร์เซ็นต์และอัตราส่วนที่ไม่ได้จำกัด: ค่าธรรมเนียม, LTV, ส่วนลด, สลิปเพจ หรือเรทรางวัล ที่เกินขอบเขตที่กำหนดไว้
  • ความสัมพันธ์ของอาร์เรย์ที่ไม่ได้รับการตรวจสอบ: อาร์เรย์สองอันมีความยาวต่างกัน ค่าที่ซ้ำซ้อน หรือเส้นทางที่ซ้ำซ้อนกับทรัพย์สินที่ไม่คาดคิด
  • สถานะการเปลี่ยนแปลงโดยไม่มีข้อกำหนดล่วงหน้า: การเรียกร้อง การยกเลิก การไถ่ถอน หรือการย้ายถิ่นฐานในสถานะไม่เหมาะสม
  • ข้อมูลภายนอกที่รับเป็นข้อเท็จจริง: ฟังก์ชันเชื่อถือราคา ยอด balances ลายเซ็น หรือค่าที่ส่งกลับโดยไม่ตรวจสอบ

การ validate ต้องอยู่ในบริบทที่เหมาะสม เช่น require(amount > 0) เป็นสิ่งที่มีประโยชน์ แต่ไม่สามารถพิสูจน์ได้ว่า amount นั้นสามารถรองรับได้ เป็นทรัพย์สินค้ำประกัน หรืออยู่ในขอบเขตของสถานะปัจจุบัน รวมถึงการตรวจสอบว่า address ของโทเคนไม่เป็น zero ก็ไม่ได้พิสูจน์ว่า โทเคนได้รับการอนุญาตหรือเป็นไปตามคาดหรือเป็นของตลาดที่ถูกต้อง

วิธีการตรวจทานของ Soken จะแทบทุก parameter ภายนอกเป็นการพยายามเปลี่ยนแปลงสถานะ ค้นหา invariant ที่สามารถกระทบ และตรวจสอบว่า ฟังก์ชันบังคับใช้ invariant นั้นก่อนที่จะเขียนข้อมูลลง storage

ตัวอย่างการโจมตี validate input ใน Solidity เป็นอย่างไร?

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

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

interface IERC20 {
    function transfer(address to, uint256 value) external returns (bool);
}

contract UnsafeLender {
    IERC20 public immutable asset;
    mapping(address => uint256) public debt;

    constructor(IERC20 asset_) {
        require(address(asset_) != address(0), "zero asset");
        asset = asset_;
    }

    function borrow(
        uint256 collateralValue,
        uint256 amount
    ) external {
        require(collateralValue > 0, "no collateral");
        require(amount <= collateralValue, "insufficient collateral");

        // จุดอ่อน: ผู้กู้ให้ค่าค้ำประกันตามที่ต้องการ
        debt[msg.sender] += amount;

        require(asset.transfer(msg.sender, amount), "transfer failed");
    }
}

แนวทางแก้ไข ควรจะให้ borrowing capacity มาจากทรัพย์สินที่สัญญาได้รับจริง ๆ โดยใช้รายการ allowlist ที่เข้มงวด ตัวตรวจสอบใน constructor คัดกรอง address เป็น address ที่ไม่ใช่ zero และใช้การเช็ค LTV เป็นขอบเขต และการตรวจสอบ delta ยอดคงเหลือช่วยให้แน่ใจว่ายอดโอนตรงกับยอดรับจริง ไม่ใช่เพียงพึ่งพาในค่า deposit ที่ร้องขอ

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

interface IERC20 {
    function balanceOf(address account) external view returns (uint256);
    function transfer(address to, uint256 value) external returns (bool);
    function transferFrom(address from, address to, uint256 value) external returns (bool);
}

contract ValidatedLender {
    IERC20 public immutable loanAsset;
    IERC20 public immutable collateralAsset;
    uint256 public immutable maxLtvBps;

    mapping(address => uint256) public collateral;
    mapping(address => uint256) public debt;

    constructor(IERC20 loanAsset_, IERC20 collateralAsset_, uint256 maxLtvBps_) {
        require(address(loanAsset_) != address(0), "zero loan asset");
        require(address(collateralAsset_) != address(0), "zero collateral asset");
        require(maxLtvBps_ > 0 && maxLtvBps_ <= 10_000, "invalid LTV");
        loanAsset = loanAsset_;
        collateralAsset = collateralAsset_;
        maxLtvBps = maxLtvBps_;
    }

    function depositCollateral(uint256 amount) external {
        require(amount > 0, "zero amount");
        uint256 beforeBalance = collateralAsset.balanceOf(address(this));
        require(collateralAsset.transferFrom(msg.sender, address(this), amount), "transfer failed");
        uint256 received = collateralAsset.balanceOf(address(this)) - beforeBalance;

        // แน่ใจว่าเฉพาะทรัพย์สินที่ได้รับเท่านั้น
        require(received == amount, "unsupported token behavior");
        collateral[msg.sender] += received;
    }

    function borrow(uint256 amount) external {
        require(amount > 0, "zero amount");
        uint256 limit = (collateral[msg.sender] * maxLtvBps) / 10_000;
        require(debt[msg.sender] + amount <= limit, "borrow limit exceeded");

        debt[msg.sender] += amount;
        require(loanAsset.transfer(msg.sender, amount), "transfer failed");
    }
}

หากโปรโตคอลของคุณรับ asset, route หรือข้อมูลทางการเงินแบบยืดหยุ่น การตรวจสอบโดย adversarial smart-contract audit and penetration-testing ก็สามารถตรวจสอบว่าค่าที่อ้างนั้นได้รับการยืนยันอิสระก่อนที่จะส่งผลต่อทรัพย์สิน ถ้าต้องการปรับปรุงด้านความปลอดภัย

คุณจะป้องกันการโจมตี validate input อย่างไร?

การป้องกันการโจมตีโดย validate input ต้องตรวจสอบค่าที่ป้อนเข้ามา ณ จุดเริ่มต้น แล้วคงสมมุติฐานเดิมไว้อย่างชัดเจนตลอดวงจรชีวิตของสัญญา วิธีดีที่สุดคือไม่ใช่การสร้างตัวอยู่ใน modifier เดียว แต่เป็นการออกแบบที่ทุกการเปลี่ยนแปลงสถานะสำคัญต้องมีชุดของ input ที่อนุญาตและเงื่อนไขที่สามารถทดสอบได้ว่าเป็นจริงอยู่เสมอ

เทคนิค สิ่งที่ป้องกัน ค่าความจุกจิก / ค่าใช้จ่าย ข้อจำกัด
Allowlist สำหรับ token และ implementation ERC-20 ที่ไม่รองรับ, Adapter ที่เป็นอันตราย, การเรียกภายนอกที่ไม่ปลอดภัย การค้นหาใน storage หรือเปรียบเทียบ address แบบ immutable โทเคนที่อนุมัติแล้วอาจยังเสี่ยงด้านเศรษฐกิจได้
การตรวจ bounds จำนวน zero, อัตราสูงเกินไป, basis points ที่ผิด, เวลาที่ไม่ปลอดภัย ค่าน้อย คำเปรียบเทียบไม่กี่ตัว Bounds อย่างเดียวไม่สามารถรับรองความถูกต้องทางเศรษฐกิจได้
การตรวจ address zero และ recipient การเผาทรัพย์สิน, การใช้งานเจ้าของไม่ได้, การส่งไปยัง address ที่ไม่ถูกต้อง ค่าตรวจสอบ address หนึ่งตัว address ที่ไม่ใช่ zero ก็ยังไม่น่าเชื่อถือได้เสมอ
การตรวจสิทธิ์และสถานะวงจรชีวิต การเรียกในสถานะผิด หรือโดย actor ที่ไม่ถูกต้อง การอ่าน storage และ role design ต้องการการบริหารจัดการที่ปลอดภัย
การ validate delta ยอดเงิน การเครดิตปลอม การโอนที่ไม่สมบูรณ์ของโทเคน การอ่านยอดสมดุล token เพิ่มเติม ต้องจัดการกับ asset ที่มีการ rebasing อย่างระมัดระวัง
การ validate invariants ความเสี่ยงด้าน solvency, การสำรอง, หนี้สิน, ความสัมพันธ์สำรอง การอ่านและคำนวณเพิ่มเติม invariants ต้องครอบคลุมโมเดลทางเศรษฐกิจทั้งหมด

ในกรณีโปรโตคอลที่สามารถปรับแต่งได้ ควรตรวจสอบ configuration ทั้งตอนสร้างและตอนใช้งาน ค่าพารามิเตอร์ที่ตั้งโดย governance ก็อาจปลอดภัยในตอนสร้าง แต่ไม่ปลอดภัยหาก dependencies เปลี่ยนแปลง สำหรับระบบที่มีการอัปเกรด ควร validate parameters ของ initializer ด้วยความเข้มงวดเท่ากับ input ของ public function

ข้อมูลเชิงลึกด้านความปลอดภัย: วิธีของ Soken เริ่มจาก invariant ไม่ใช่แค่ require หากผู้นตรวจไม่สามารถระบุได้ว่าสถานะใดต้องยังคงเป็นจริงหลังจากรับ input ที่ควบคุมโดยผู้ใช้ ก็ไม่สามารถบอกได้ว่าการ validate นั้นเพียงพอหรือไม่

เหตุการณ์จริงที่จัดประเภทเป็น Missing Input Validation คืออะไรบ้าง?

เหตุกาณ์ Missing Input Validation ถูกเชื่อมโยงกับความเสียหายของ smart contract ในหลายบริบท ตั้งแต่การให้กู้ การแลกเปลี่ยน ไปจนถึงการบริหารสินทรัพย์ เหตุการณ์เหล่านี้ครอบคลุมหลาย chain และหลายดีไซน์โปรโตคอล ซึ่งแสดงให้เห็นว่า ปัญหานี้เป็นข้อผิดพลาดด้านตรรกะโปรโตคอล ไม่ใช่ความผิดของ Solidity ที่เป็นเฉพาะเครือข่าย

Protocol วันที่ จำนวนเงินที่สูญเสีย Chain
Qubit 2022-01-28 $80.0M BSC
Hedgey 2024-04-19 $44.7M Arbitrum, Ethereum
Transit Swap 2022-10-02 $28.9M Ethereum, BSC
Pickle 2020-11-22 $19.7M Ethereum

ความรู้จากเหตุการณ์เหล่านี้คือ: สัญญาไม่สามารถสันนิษฐานความจริงของโปรโตคอลจากเพียงค่าที่ encode แล้วผ่านการทำธุรกรรมอย่างสำเร็จเท่านั้น สินทรัพย์ในระบบต้องผ่านการ validate ทางความหมาย เช่น ทรัพย์สินที่ได้รับ การรองรับเส้นทางที่ปลอดภัย การอนุญาตเท่านั้น หรือการเปลี่ยนผ่านตาม lifecycle ที่ถูกต้อง

DeFiLlama จัดกลุ่มเหตุการณ์ smart contract กว่า 52 รายการตั้งแต่ปี 2020 ถึง 2026 ภายใต้ Missing Input Validation โดยมีการสูญเสียร่วมกันมากถึง $330.5M เหตุผลที่ Soken ให้ความสำคัญกับกระบวนการตรวจสอบ validation ว่าเป็นทั้งด้านเศรษฐกิจและความปลอดภัย ไม่ใช่การตรวจสอบแบบ checklist ของ require ที่ผิวเผิน

คุณจะตรวจจับช่องโหว่การ validate input ที่ผิดพลาดได้อย่างไร?

สามารถตรวจจับช่องโหว่การ validate input ที่ผิดพลาดได้โดยการ trace parameters ภายนอกทั้งหมดไปยังการเขียนข้อมูล การเรียกภายนอก และการโอนทรัพย์สินที่มันส่งผล หลังจากนั้นทดสอบว่าค่า ABI ที่ผิด แต่ยัง valid ตาม protocol สามารถละเมิด invariant หรือไม่ การวิเคราะห์แบบ static ช่วยหา check ที่หายไป แต่การทดสอบตาม scenario เป็นสิ่งจำเป็นสำหรับสมมุติฐานของโปรโตคอลเฉพาะ

แนวทางการตรวจสอบเบื้องต้น:

  1. รายการทุก parameter ของ external และ public ที่ส่งผลต่ค่า อำนาจ หรือเส้นทาง
  2. ตรวจสอบว่าทุก address เป็น nonzero, ได้รับการอนุมัติ, และเหมาะสมกับบทบาทหรือไม่
  3. ตรวจสอบ amount กับขีดจำกัดของ protocol, ยอดคงเหลือ, ขีดจำกัดหนี้หรือสมมุติฐาน decimals
  4. ยืนยันว่าราคา, มูลค่าทรัพย์สิน, ยอด balance มาจากแหล่งข้อมูลที่เชื่อถือได้หรือได้รับการตรวจสอบโดยอิสระ
  5. ตรวจสอบความยาว array, ความเป็นเอกลักษณ์, ลำดับ และความสัมพันธ์ของ index ข้าม array
  6. รับรองว่าฟังก์ชันอนุญาตเฉพาะสถานะการดำเนินงานตามวงจรชีวิตและ callers ที่ได้รับอนุญาต
  7. ทดสอบการคำนวณ delta ยอดคงเหลือด้านค่าธรรมเนียม, การ rebasing, และพฤติกรรมไม่ปกติของ ERC-20
  8. ระบุและทดสอบ post-condition สำหรับทุก operation ที่เกี่ยวกับการเคลื่อนย้ายทรัพย์สิน

Slither เป็นเครื่องมือช่วยหา pattern อันตราย เช่น calls ภายนอกที่เสี่ยง การคืนค่าที่ไม่ได้ตรวจสอบ และการควบคุมการเข้าถึงที่อ่อนแอ ซึ่งมักครอบคลุมความล้มเหลวในการ validate แต่ก็ไม่สามารถสรุป invariants ของ protocol ได้ทั้งหมด จึงควรใช้ Foundry fuzzing เพื่อสร้างค่า zero, ค่าสูงสุด, arrays ซ้ำซ้อน address ไม่คาดคิด รวมถึงโทเคนที่ unsupported

การทดสอบ invariants เป็นวิธีที่มีประสิทธิภาพ เช่น ทดสอบว่ายอดหนี้คงค้างทั้งระบบไม่เคยเกินขีดจำกัดการกู้ยืม, ทรัพย์สินที่เครดิตเข้ามาไม่เกินมูลค่าที่ได้รับ และเฉพาะทรัพย์สินที่ได้รับการอนุมัติเท่านั้นที่จะเปลี่ยนแปลงการบันทึกบัญชีในตลาด เมื่อสร้างกระบวนการตรวจสอบความปลอดภัยแบบกว้างขึ้น ควรใช้ Soken Hub ร่วมกับการทดสอบแบบ invariant

การ validate input คือตัวกั้นระหว่างความตั้งใจของผู้ใช้กับความจริงของโปรโตคอล สัญญาควรไม่เคยอนุญาตให้เครดิต อนุญาต ราคา หรือโอนโดยอาศัยข้อมูลที่อ้างโดยผู้เรียกเพียงอย่างเดียว ควรจะสร้างค่าที่อ่อนไหวจากข้อมูลที่ได้รับการยืนยัน สกัดกั้น domain ที่อนุญาต และบังคับให้อยู่ใน invariant ที่ทำให้สถานะท้ายปลอดภัยเชิงเศรษฐกิจ

แนวทางขั้นตอนถัดไปคือการสร้าง map parameter-to-invariant สำหรับทุกฟังก์ชันที่เคลื่อนย้ายทรัพย์ในโค้ดของคุณ ค่า parameter ที่ไม่มีแหล่งความเชื่อถือ มีช่วง permitted หรือไม่มี post-condition ควรได้รับการตรวจสอบอย่างเร่งด่วน

ผู้เขียนบทความ

คำถามที่พบบ่อย

ช่องโหว่การตรวจสอบอินพุตใน Solidity คืออะไร?

ช่องโหว่การตรวจสอบอินพุตใน Solidity เกิดขึ้นเมื่อ smart contract ยอมรับ addresses, จำนวนเงิน, ตัวระบุ, ราคาหรือค่าการตั้งค่าโดยไม่ได้ตรวจสอบว่าตรงตามสมมติฐานด้านเศรษฐกิจและความปลอดภัย ซึ่งอาจทำให้เกิดการดำเนินการผิดพลาด เช่น การโอนสินทรัพย์อย่างไม่ปลอดภัย หรือการบันทึกบัญชีผิดพลาด

ฉันจะป้องกันการโจมตี Input Validation ใน Solidity ได้อย่างไร?

เพื่อป้องกันการโจมตี Input Validation ใน Solidity ควรตรวจสอบทุกอินพุตภายนอกที่ขอบเขตของ trust ก่อนการโอนสินทรัพย์หรือเปลี่ยนแปลงบัญชี รวมทั้งใช้ allowlists, bounds, zero-address checks, การตรวจสอบการอนุญาต และ invariants เพื่อให้ค่าที่เกี่ยวข้องเป็นไปตามกฎความปลอดภัยและเศรษฐกิจที่ตั้งไว้

เกิดอะไรขึ้นในการโจมตี Input Validation ของ Qubit?

Qubit สูญเสียเงิน 80 ล้านดอลลาร์บน BSC เมื่อวันที่ 28-01-2022 เหตุการณ์นี้จัดเป็น Missing Input Validation ซึ่งแสดงให้เห็นว่า inputs ที่ไม่ได้ตรวจสอบอาจทำให้ contract จดจำ collateral ที่ไม่มีอยู่จริงหรือดำเนินการทางเศรษฐกิจที่ไม่ถูกต้อง

ทำไม smart contract ใน Solidity ต้องตรวจสอบ zero address?

smart contract ควรปฏิเสธ zero address เมื่อ address ที่ผู้ใช้ให้มาต้องบ่งชี้ถึงปลายทาง ทรัพย์สิน หรืออำนาจจริง เพราะการตรวจสอบ zero address จะป้องกันไม่ให้ contract ยอมรับค่าตัวแทน (placeholder) ที่ละเมิดสมมติฐานของการโอน อนุญาต หรือบัญชี

bounds checks เพียงพอไหมสำหรับการป้องกันช่องโหว่ Input Validation?

Bounds checks เพียงอย่างเดียวไม่เพียงพอ เนื่องจากค่าที่อยู่ในช่วงตัวเลขอาจยังฝ่าฝืนเงื่อนไขการอนุญาต allowlist ที่อยู่ หรือ invariants ของสถานะ จึงต้องมีการ validate layered ที่ขอบเขต trust เพื่อให้แน่ใจว่าค่าที่ได้รับสอดคล้องกับกฎธุรกิจของแต่ละอินพุต

แชท