قراردادهای هوشمند برنامههایی هستند که مستقیماً دارایی واقعی را مدیریت میکنند. برخلاف نرمافزار معمولی، کد منتشرشده روی زنجیره عمومی برای همه قابلمشاهده است و مهاجمان زمان کافی برای تحلیل آن دارند. به همین دلیل امنیت باید پیش از انتشار تضمین شود.
۱. حمله ورود مجدد (Reentrancy)
وقتی قرارداد قبل از بهروزرسانی وضعیت داخلی، وجه را به آدرس خارجی ارسال میکند، قرارداد مهاجم میتواند در همان فراخوانی دوباره تابع برداشت را صدا بزند.
// ناامن
function withdraw() external {
uint amount = balances[msg.sender];
(bool ok,) = msg.sender.call{value: amount}("");
require(ok);
balances[msg.sender] = 0; // دیر!
}
// امن: Checks-Effects-Interactions
function withdraw() external nonReentrant {
uint amount = balances[msg.sender];
balances[msg.sender] = 0;
(bool ok,) = msg.sender.call{value: amount}("");
require(ok);
}
۲. کنترل دسترسی ناقص
توابع حساس مانند mint، pause یا تغییر مالک باید محدود به نقشهای مشخص باشند. استفاده از الگوهای نقشمحور و چندامضایی (Multisig) برای عملیات مدیریتی ضروری است.
۳. سرریز و خطای محاسباتی
در زبانها و نسخههایی که بررسی خودکار سرریز ندارند، جمع و تفریق بدون کنترل میتواند موجودی را به عددی عجیب تبدیل کند. همچنین ترتیب ضرب و تقسیم در محاسبات کارمزد باعث خطای گردکردن میشود.
۴. دستکاری اوراکل قیمت
اتکا به قیمت لحظهای یک استخر نقدینگی کوچک، به مهاجم اجازه میدهد با یک وام آنی (Flash Loan) قیمت را جابهجا کند. از اوراکلهای غیرمتمرکز و میانگین زمانی (TWAP) استفاده کنید.
۵. Front-running
تراکنشها پیش از تأیید در صف عمومی دیده میشوند. الگوی Commit–Reveal و تعیین حداکثر لغزش قیمت، اثر این حمله را کاهش میدهد.
۶. وابستگی به زمان و تصادفیسازی ضعیف
مقادیری مانند زمان بلوک قابل پیشبینی یا تا حدی قابل دستکاریاند و نباید منبع تصادفی بودن در قرعهکشی یا بازی باشند.
۷. منطق ارتقاپذیری ناامن
قراردادهای پراکسی اگر بهدرستی مقداردهی نشوند یا چیدمان حافظه را در نسخه جدید تغییر دهند، ممکن است کنترل قرارداد به دست مهاجم بیفتد.
۸. انکار سرویس با حلقههای بیکران
حلقه روی آرایهای که کاربران میتوانند بزرگش کنند، ممکن است هزینه اجرای تابع را از سقف مجاز فراتر ببرد و قرارداد را قفل کند.
فرایند امن توسعه
- مدل تهدید و تعیین داراییها و نقشها پیش از کدنویسی
- استفاده از کتابخانههای استاندارد و آزمودهشده
- تست واحد، تست Fuzz و پوشش سناریوهای مرزی
- تحلیل ایستا با ابزارهای خودکار
- ممیزی مستقل و رفع یافتهها پیش از انتشار
- انتشار تدریجی روی Testnet، برنامه پاداش باگ و پایش پس از انتشار
هزینه ممیزی همیشه کمتر از هزینه یک حمله موفق است.