ستخدم أدوات تدقيق العقود الذكية لتحديد الثغرات الأمنية في العقود الذكية . يمكن استخدام هذه الأدوات من قبل مدققي العقود الذكية أو المطورين أو أي شخص آخر يريد التأكد من أن عقودهم الذكية آمنة أو تتمتع بمستوى معين من الأمان.
في هذه المقالة، سنناقش أربعة من أبرز هذه الأدوات: Foundry، Diligence Fuzzing، Echidna، وDapptools. تم تطوير كل منهم باستخدام لغات مختلفة مثل Rust وHaskell، وتوفر ميزات فريدة لضمان سلامة العقود وأمانها. سنستعرض كيفية تثبيت واستخدام هذه الأدوات، بالإضافة إلى نقاط القوة والضعف المرتبطة بكل منها.
كما سبق و تحدثنا عن في مقالات سابقة عن أدوات التحليل الثابت، وأدوات إختبار الطفرة ، وأدوات التحقق الرسمية، وأدوات التصور.
ملاحظة: تحتوي بعض هذه الأدوات على متطلبات Python غير متوافقة عند التثبيت، لذا كن حذرًا عند استخدامها على نفس الجهاز. تفترض هذه المقالة إتقان لغة Solidity.
اختبار التشويش والثبات (Fuzzing and Invariant)
يعد اختبار التشويش (Fuzzing test) تقنية أخرى يمكن استخدامها للعثور على نقاط الضعف في العقود الذكية. في اختبار التشويش، يتم استدعاء وظائف العقد الذكي بمدخلات عشوائية وغير متوقعة. يمكن أن يؤدي هذا إلى كشف السيناريوهات التي يتعطل فيها النظام.
من ناحية أخرى، يركز اختبار الثبات (invariant test) على التحقق من خصائص أو ثوابت محددة. هذه هي الشروط التي يجب أن تكون صحيحة دائمًا طوال فترة استخدام العقد الذكي.
كلتا طريقتي الاختبار عبارة عن تحليل ديناميكي، حيث يتم تنفيذ كود العقد الذكي فعليًا أثناء الاختبار.
1. Consensys Diligence Fuzzing
ConsenSys Diligence Fuzzing هي أداة اختبار تشويش لتحديد العيوب في العقود الذكية. وهو جزء من مجموعة أدوات الأمان ConsenSys Diligence.
يحتوي ConsenSys Diligence fuzzing على أداة CLI التي تسمح للمستخدمين بتشغيل التشويش محليًا.
كما أن لديها لوحة تحكم سحابية ، والتي توفر منصة مركزية لتنظيم ومراقبة أنشطة التشويش. يتضمن ذلك أدوات قيمة لتقييم نتائج حملة التشويش .
تثبيت:
يعمل الـ CLI على Python 3.6+ ، بما في ذلك 3.8 وpypy3.
لتثبيت Diligence Fuzzing CLI قم بتشغيل :
pip3 install diligence-fuzzing
mukaebقم بزيارة https://fuzzing.diligence.tools/login لإنشاء حساب ومشروع Fuzzing
كيفية الاستخدام:
تعمل تقنية Diligence Fuzzing على تعزيز التعليقات التوضيحية للعقود الذكية المكتوبة بلغة Scribble ، وهي لغة مصممة لتحديد خصائص وقيود الأمان. يقوم Fuzzer بفحص هذه الممتلكات والإبلاغ عن أي انتهاكات.
لمعرفة المزيد حول Scribble ، قم بزيارة الوثائق .
عادة ما تكون التعليقات التوضيحية لـ Scribble مكتوبة بهذه الطريقة :
if_succeeds {:msg "<Description of property>"} <boolean scribble expression>;
mukaeb- if_succeeds هو بداية التعليق التوضيحي ويشير إلى أنه يجب الاحتفاظ بخاصية معينة بعد تنفيذ الوظيفة بنجاح.
- {:msg “<Description of property>”} هو جزء اختياري حيث يمكننا وصف الخاصية التي يحددونها. يساعد هذا في إنشاء توثيق وفهم التعليق التوضيحي بشكل أفضل.
- <boolean scribble expression> هو الجزء الأساسي من التعليق التوضيحي الذي يمثل الخاصية أو القيود التي نريد تحديدها. يمكن أن تكون x >= y، BalanceOf(msg.sender) <= TotalSupply، إلخ.
من أجل تنفيذ نقل أصل ERC20 بشكل صحيح، سيبدو التعليق التوضيحي البسيط كما يلي:
contract ERC20example {
/// if_succeeds {:msg "ERC20 transfer"} balance(msg.sender) == old(balanceOf(msg.sender)) - amount;
function transfer(address recipient, uint256 amount) public {
// ...
}
}
mukaebونحن هنا نقول أنه إذا نجحت عملية النقل، فيجب خصم رصيد المرسل
balance(msg.sender) == old(balanceOf(msg.sender)) – amount
سيحاول الـ Fuzzer العثور على مدخلات عشوائية من شأنها كسر هذه الخاصية.
يمكنك معرفة المزيد حول إنشاء حملات تشويشه وكتابة التعليقات التوضيحية هنا .
استخدام Diligence fuzzin مع foundry
يدعم Diligience fuzzing أيضًا اختبار التشويش من foundry. يمكننا إنشاء مشروع اختبار التشويش من foundry واستخدام Diligence fuzz CLI لإنشاء حملة لاختبارfoundry.
دعونا نرى مثالا أدناه.
قم بتشغيل الأمر التالي لإنشاء مشروع foundry جديد:
forge init diligence-fuzz-foundry
cd diligence-fuzz-foundry
mukaebبمجرد إنشاء مشروعنا، قم بتحديث الأكواد المرفقة كما يلي:
أعد تسمية Counter.sol في مجلد src إلىPassword.sol والصق هذا:
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
contract Password {
// Secret password
uint256 private password;
constructor() payable {
require(msg.value >= 1 ether);
// Set secret password as 25
password = 25;
}
function withdraw(uint256 password_) public {
// If anyone guesses the correct password, they can take all the ether in this contractif (password_ == password) {
(bool success, ) = msg.sender.call{value: address(this).balance}(
""
);
require(success, "failed to send");
}
}
function balance() public view returns (uint) {
return address(this).balance;
}
receive() external payable {}
}
mukaebأعد تسمية Counter.t.sol في مجلد الاختبار إلى كلمة المرور.t.sol والصق هذا:
// SPDX-License-Identifier: UNLICENSEDpragma solidity ^0.8.13;import "forge-std/Test.sol";
import "../src/Password.sol";
contract PasswordTest is Test {
Password code;
function setUp() public {
// Deploy the contract with 1 ether
code = new Password{value: 1 ether}();
}
function testFuzz_Password(uint256 password_) public {
// We are fuzzing the `password_` input,// foundry fuzzer will try to find a number that matches our password and breaks the assert statement below,// which means all the ether from the contract has been taken
code.withdraw(password_);
assert(code.balance() == 1 ether);
}
receive() external payable {}
}
mukaebلن نقوم باختبار هذا مع foundry لأنه ليس هدف هذا القسم.
لدينا كل ما نحتاجه للإعداد. بعد ذلك، توجه إلى لوحة معلومات Diligence Fuzzer وقم بإنشاء مفتاح API.
انقر فوق “إنشاء مفتاح API جديد”
انسخ مفتاح API وقم بتشغيل هذا الأمر في مشروع foundry:
fuzz forge test -k <your-api-key-here>
# Without the `<>` sign
mukaebنحصل على هذه النتيجة عندما نقوم بتشغيل الأمر.
يمكننا الضغط على الرابط المطبوع على الجهاز، وهذا سيأخذنا إلى الحملة، كما هو موضح أدناه:
لدينا خاصية فاشلة!
تمكنت حملة Fuzz من اكتشاف الثغرة الأمنية في وظيفة “testFuzz_password” كما هو موضح في الصورة أعلاه.
للحصول على مزيد من التفاصيل حول الخاصية الفاشلة، يمكننا النقر على زر التوسيع.
نرى التأكيد (code.balance() == 1 ether) الذي تحته خط؛ حيث تمكن التشويش من كسر هذه الخاصية عن طريق تشويش كلمة المرور السرية.
يمكننا أيضًا أن نرى أنه تم تمرير “25” إلى “testFuzz_Password” في خطوات المعاملة أدناه
لمعرفة المزيد حول استخدام Diligence Fuzz مع foundry، قم بزيارة الوثائق هنا .
نقاط القوة
- Diligence fuzz فعال في العثور على الأخطاء.
- يأتي Diligence Fuzz CLI مع أوامر المساعدة لاختبار Fuzz، مثل Fuzz Arm إلى لكتابة أومر توضيحية لعقود solidity بإستخدام scribble و Fuz disarm لإعادة التغييرات إلى الحالة الأصلية .
- إنه قابل للتطوير ويمكن استخدامه لتشويش المشاريع الكبيرة.
- يمكن إجراء التشويش محليًا أو في السحابة.
- لديه دعم لاختبار التشويش من foundry.
نقاط الضعف
- ليس لديها إعداد سهل.
- يمكن أن يكون الاختبار بطيئًا في بعض الأحيان.
- يتطلب اشتراكًا وليس مجانيًا تمامًا للاستخدام.
- جزء من هذه الأداة قائم على السحابة ومركزي، لذلك هناك قلق بشأن خصوصية البيانات.
- لا يزال قيد التطوير ولا يحظى بدعم مجتمعي نشط.
2. اختبار الثبات من foundry
Foundry عبارة عن أداة سريعة لتطوير العقود الذكية تم إنشاؤها باستخدام Rust.
تتعامل مع تبعيات المشروع، ويجمع المشاريع، ويجري الاختبارات، وينشر، ويسمح لك بالتفاعل مع blockchain عبرSolidity scripts و command line.
التثبيت:
تثبيت foundry:
curl -L https://foundry.paradigm.xyz | bash
mukaebأعد تشغيل terminal وقم بتشغيل Foundryup لتثبيت Forge وAnvil وCast وChisel.
إذا واجهت أي مشكلة، راجع الأسئلة الشائعة .
كيفية الاستخدام:
قمنا بكتابة مقالة شاملة بعنوان إختبار الثبات في foundry و التي تتعمق في هذا الموضوع
نقاط القوة
- الاختبار باستخدام Foundry سريع.
- هناك أكواد غش وتخصيصات مفيدة يمكن استخدامها لكتابة اختبارات أكثر كفاءة، مثل زيادة عدد مرات تشغيل اختبار Fuzz.
- يحتوي Foundry أيضًا على طريقة اختبارات handler-based وهي مفيدة لإجراء اختبار ثابت يتضمن تفاعلات عبر العقود المختلفة .
نقاط الضعف
- التحديد اليدوي لنطاق الإدخال: في بعض الأحيان قد تحتاج إلى ربط نطاق الأرقام العشوائية المدخلة يدويًا لتحقيق النتيجة المرجوة. وذلك لأن Foundry قد لا يتمكن من التقاط قيمة الإدخال الصحيحة من تلقاء نفسه.
3 . Echidna
Echidna هي أداة اختبار أمان تشويش ومبنية على الملكية لعقود Ethereum الذكية المكتوبة بلغة Haskell.
إنه يزيف المسندات المحددة من قبل المستخدم أو عبارات تأكيد Solidity باستخدام حملات تشويش معقدة قائمة على القواعد تعتمد على ِ ABI العقد.
التثبيت:
يتطلب Echidna تثبيت Slither، لذا تأكد من تثبيت Slither قبل متابعة التثبيت.
إذا لم يتم تثبيت Slither مسبقًا، فقم بتشغيل الأمر التالي:
pip3 install slither-analyzer
mukaebلتثبيت Echina، قم بتشغيل:
brew install echidna
mukaebأو البناء من binaries.
كيفية الاستخدام:
Echidna هي أداة موثوقة للعقود الذكية. تقوم بتحليل العقود الذكية بناءً على ثوابت محددة مسبقًا.
تقوم بإنشاء تسلسلات عشوائية من استدعاءات العقد لاختبارها مقابل الثوابت. إذا اكتشف Echidna تسلسل اتصال ينتهك أحد الثوابت، فإنه يقوم بالإبلاغ عنه. إذا لم يتم اكتشاف مثل هذا التسلسل، فهذا يعطي الثقة بأن العقد آمن بناءً على جوانب الثوابت المحددة مسبقًا على الرغم من أن هذا لا يعني أنه لن تكون هناك عيوب في الكود.
يتم تحديد ثوابت Echidna بواسطة وظيفة.
تبدأ هذه الوظائف بالكلمة الأساسية echidna_، ولا تأخذ أي معلمات إدخال ويجب أن تُرجع قيمة منطقية. من خلال هذا، يعرف Echidna ما هو الثابت الذي سيحاول كسره.
دعونا نرى مثالا أدناه:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Code {
address public owner = address(0x11EF);
modifier onlyOwner() {
require(msg.sender == owner, "not owner");
_;
}
function changeOwner(address newOwner) public {
owner = newOwner;
}
// Invariantf
unction echidna_no_address_zero() public returns (bool) {
return owner != address(0x00);
}
}
mukaebعندما نقوم بتشغيل Echidna، سيحاول تزوير الثابت داخل وظيفة “echidna_no_address_zero”.
قم بتشغيل هذا الأمر:
echidna Code.sol
mukaebنحصل على هذا الإخراج:
لقد حددنا مُعدِّل “onlyOwner” ونسينا استخدامه في وظيفة “changeOwner”.
ولذلك، يمكن لأي شخص استدعاء الدالة وتعيين العنوان (0x00) كالمالك، وبالتالي كسر الثابت owner != address(0x00).
تمكنت Echidna من اكتشاف هذا الخطأ وتغيير عنوان المالك.
راجع هذا الريبو إذا كنت تريد استخدام Echidna مع foundry .
نقاط القوة
- Echidna عبارة عن أداة تشويش قوية يمكنها اكتشاف نقاط الضعف في العقود الذكية.
- يمكن لـ Echidna اختبار العقود المجمعة باستخدام أنظمة بناء عقود ذكية مختلفة، بما في ذلك Truffle أو Hardhat باستخدام crytic-compile. لاستدعاء echidna باستخدام الـcompiler الحالي، استخدم echidna .
- Echidna سهل الاستخدام ويمكن تشغيله من سطر الأوامر.
نقاط الضعف
- قد يكون Echidna بطيئًا في اكتشاف العيوب في العقود الكبيرة.
- لديها دعم محدود لاختبار العقود التي تعتمد بشكل كبير على المكتبات الخارجية، مما قد يحد من قدرات الاختبار الخاصة بها في مثل هذه الحالات.
- دعمها للغة برمجة Vyper محدود.
- Echidna ليست مثالية وقد تفوت بعض الأخطاء في العقود الذكية.
4. Dapptools
Dapptools عبارة عن مجموعة من أدوات CLI التي تركز على Ethereum. يتضمن أداة تحليل ثابتة للعقود الذكية، بالإضافة إلى عدد من الأدوات الأخرى لاختبار وتصحيح العقود الذكية مثل:
- dapp – إنشاء عقود solidity واختبارها والتحقق منها رسميًا وتصحيحها ونشرها.
- seth – إيثريوم CLI. الاستعلام عن العقود، وإرسال المعاملات، ومتابعة السجلات، وتقطيع البيانات.
- hevm – اختبار تنفيذ EVM الموجه. تصحيح الأخطاء أو التشويش أو تنفيذ الكود بشكل رمزي مقابل الحالة المحلية أو حالة الشبكة الرئيسية.
- ethsign – قم بتوقيع معاملات Ethereum من مخزن المفاتيح المحلي أو محفظة الأجهزة.
التثبيت:
تثبيت Nix أولاً:
sh <(curl -L https://nixos.org/nix/install) --daemon
mukaebاستخدم Nix لتثبيت Dapptools:
nix profile install github:dapphub/dapptools#{dapp,ethsign,hevm,seth}
mukaebإذا واجهت مشكلات أثناء محاولة تثبيت Dapptools على جهاز Mac، فإليك خطوة رائعة يجب اتباعها https://roycewells.io/writing/dapptools-m1/ .
كيفية الاستخدام:
يمكنك استخدام Dapptools لاختبار الوحدة والاختبار القائم على التشويش أو الخاصية والاختبار الثابت وحتى التنفيذ الرمزي.
سنقوم بإجراء اختبار زغبي وثابت.
اختبار Fuzz قم بتشغيل الأوامر التالية:
mkdir dapp-fuzz-test
cd dapp-fuzz-test
dapp init
mukaebالآن احذف ملفات Solidity الافتراضية الموجودة داخل دليل src/.
قم بإنشاء ملفين جديدين، Code.sol و Code.t.sol والصق الكود التالي على التوالي:
- Code.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;
contract Code {
uint256 private password;
constructor() payable {
require(msg.value >= 1 ether);
password = 25;
}
function withdraw(uint256 password_) public {
if (password_ == password) {
(bool success, ) = msg.sender.call{value: address(this).balance}(
""
);
require(success, "failed to send");
}
}
function balance() public view returns (uint) {
return address(this).balance;
}
}
mukaebCode.t.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.6;
import "ds-test/test.sol";
import "./Code.sol";
contract CodeTest is DSTest {
Code code;
function setUp() public {
code = new Code{value: 1 ether}();
}
function test_fuzz(uint256 password_) public {
code.withdraw(password_);
assert(code.balance() == 1 ether);
}
receive() external payable {}
}
mukaebهنا ننشر عقد الكود الذي يحتوي على 1 إيثر و25 كرقم سري.
لسحب هذا الإيثر، يجب على المشوش إدخال الرقم السري الصحيح وهو متغير خاص. لاحظ أن وحدة التشويش لا تقرأ فتحات التخزين وتقوم فقط بتمرير أرقام عشوائية لكسر التأكيد (code.balance() == 1 ether)؛ داخل وظيفة الاختبار test_fuzz(uint256password_).
عادةً ما تأخذ وظائف اختبار Fuzz في Dapptools معلمة واحدة على الأقل. ستكون هذه المعلمة عشوائية في كل تشغيل للاختبار.
قم بإجراء الاختبار باستخدام dapp test.
يعرف Dapp تلقائيًا أنه اختبار تشويش ويعطي النتيجة التالية:
يمكننا أن نرى أن الاختبار نجح ولم يتمكن الـ Fuzzer من الحصول على الرقم السري الصحيح.
الحل الأمثل هو زيادة عدد مرات تشغيل التشويش.
للقيام بذلك سوف نستخدم علامة –fuzz-runs <number> .
على سبيل المثال، إذا قمنا بإعادة تشغيل الاختبار باستخدام dapp test –fuzz-runs 5000، فسنحصل على ما يلي:
قمنا بزيادة عدد مرات الجري، وتمكن Fuzzer من الحصول على رقمنا السري.
يمكننا إعادة تشغيل المعاملة باستخدام
dapp test --replay '("test_fuzz(uint256)","0x0000000000000000000000000000000000000000000000000000000000000019")' --verbosity 3
mukaebتهدف علامة –verbosity <number> إلى زيادة تفاصيل الاختبار حتى نتمكن من رؤية آثار المعاملة.
نرى أن الدالة Code.balance() أعادت صفرًا، مما أدى إلى تراجع الاختبار وفشله.
يعمل الاختبار الثابت في dapp أيضًا بشكل مشابه لاختبار Foundry، تعرف على المزيد هنا .
نقاط القوة
- باستخدام dapp، يمكننا إجراء اختبار الوحدة، واختبار التشويش، والاختبار الثبات ، والتنفيذ الرمزي، والاختبار مقابل حالة RPC.
- يمكننا إعادة تشغيل الاختبار الفاشل وحتى تصحيحه لرؤية التنفيذ على EVM، وتصور المكدس والذاكرة.
- لا يقوم Dapp بتنفيذ المعاملات باستخدام RPC. بدلا من ذلك، فإنه يستدعي مباشرة hevm cli. يعد هذا أسرع ويوفر قدرًا كبيرًا من التنوع الذي لا يوفره RPC، مثل اختبار التشويش.
- وهو يدعم أكواد الغش لتسهيل الاختبار، مثل hevm.warp(uint256) وhevm.load(address,bytes32).
نقاط الضعف
- يعد استخدام معلومات التصحيح أمرًا معقدًا وقد يكون من الصعب فهمها.
- لديها دعم مجتمعي محدود.
- يمكن أن يكون dapp بطيئًا في التشغيل.
- لديها وثائق سيئة.
- dapp fuzzer بطيء في العثور على نقاط الضعف الشائعة في عمليات التشغيل الصغيرة
ختاماً
نجد الأن أهمية استخدام الأدوات الصحيحة لتطوير واختبار هذه العقود. مع الأدوات مثل Foundry، Echidna، وDapptools، يمكننا ضمان تطوير العقود الذكية بطريقة فعالة وسليمة. ولكن، من المهم أن نعترف بأن لكل أداة نقاط قوة وضعف. لذلك، من الأساسي البحث عن الميزات التي تحتاجها واختيار الأدوات المناسبة وفقًا لذلك.
إطرح رأيك ؟
أظهر التعليقات / إنرك تعليق