حمله بازگشتی (Reentrancy Attack) در قراردادهای هوشمند؛ تهدیدی خاموش اما ویرانگر

حمله بازگشتی یا Reentrancy Attack یکی از معروفترین و خطرناکترین آسیبپذیریها در دنیای قراردادهای هوشمند است. این نوع حمله زمانی رخ میدهد که یک تابع در قرارداد شما، پیش از بهروزرسانی وضعیت داخلی خود، یک تماس خارجی (external call) برقرار میکند — معمولاً برای ارسال وجه یا تعامل با یک قرارداد دیگر.
اگر قرارداد مقصد مخرب باشد، میتواند با استفاده از این تماس، دوباره وارد همان تابع (re-enter) شود و بهصورت تکراری از همان وضعیت قبلی سوءاستفاده کند. نتیجه؟ مهاجم میتواند چندین بار اقداماتی مانند برداشت وجه را انجام دهد، قبل از اینکه وضعیت داخلی قرارداد شما بهروز شود.
حمله بازگشتی چه تبعاتی دارد؟
۱. تخلیه کامل موجودی قرارداد
شایعترین پیامد Reentrancy، سرقت تدریجی یا ناگهانی وجوه قرارداد است. مهاجم میتواند چندین بار در یک تراکنش، عملیات برداشت را تکرار کند و تمام موجودی را خالی کند — مشابه اتفاقی که در حمله معروف DAO در سال ۲۰۱۶ رخ داد.
۲. فراخوانیهای غیرمجاز و دستکاری منطق قرارداد
علاوه بر سرقت وجوه، مهاجم ممکن است بتواند توابع دیگر قرارداد را بهصورت غیرمجاز فراخوانی کند. این کار میتواند منجر به تغییر غیرمنتظره وضعیت، نقض دسترسیها یا ایجاد رفتارهای غیرقابل پیشبینی در منطق برنامه شود.
چرا حملات Reentrancy تا این اندازه خطرناک هستند؟
این نوع حمله اغلب بهصورت زنجیرهای و در یک تراکنش رخ میدهد، بهطوریکه توسعهدهنده حتی فرصت ندارد تا وضعیت را اصلاح کند. اگر وضعیت (state) قبل از تماس خارجی بهروزرسانی نشود، مهاجم میتواند از همان وضعیت "قدیمی" سوءاستفاده کند و یک حلقه بازگشتی از سوءاستفاده را شکل دهد.
راهکارهای جلوگیری از حمله Reentrancy
۱. ابتدا وضعیت را تغییر دهید، سپس فراخوانی خارجی انجام دهید
یکی از اصول طلایی توسعه قرارداد امن در Solidity، الگوی Checks-Effects-Interactions است. طبق این الگو، ابتدا باید:
-
تمام بررسیها (checks) را انجام دهید
-
سپس وضعیت داخلی (effects) را بهروزرسانی کنید
-
و فقط در پایان، تعامل با قرارداد خارجی (interactions) را صورت دهید
به زبان ساده: اول حساب کاربر را کم کنید، بعد پول را بفرستید.
۲. استفاده از محافظ بازگشتپذیری (Reentrancy Guard)
کتابخانه OpenZeppelin یک modifier به نام nonReentrant
ارائه میدهد که با استفاده از آن، از ورود مجدد به یک تابع خاص در حین اجرای آن جلوگیری میشود. این روش بسیار مؤثر، ساده و قابل اطمینان برای بستن درهای بازگشتی است.
۳. پرهیز از فراخوانیهای سطح پایین مگر در موارد ضروری
تا حد امکان، از متدهای سطح پایین مانند .call()
برای ارسال وجه استفاده نکنید. روشهایی مانند .transfer()
یا .send()
تا حدی محدودتر هستند و اجرای مجدد کد را سختتر میکنند (هرچند در نسخههای جدید محدودیتهایی برای آنها نیز وجود دارد).
با استفاده از codeauditplus ، در کمترین زمان و با هزینه بسیار معقول، میتوانید آسیب پذیری ها را شناسایی و در نتیجه برای بهبود کد قرارداد اقدام متناسب انجام دهید.
جمعبندی
حمله بازگشتی یکی از آسیبپذیریهای کلاسیک اما همچنان خطرناک در قراردادهای هوشمند است. با رعایت اصول سادهای مانند بهروزرسانی زودهنگام وضعیت، استفاده از محافظهای منطقی و اجتناب از تماسهای غیرضروری، میتوانید بهسادگی این تهدید را از پروژه خود دور نگه دارید.
امنیت، با توجه به همین جزییات ساخته میشود — و بیتوجهی به آنها میتواند به از دست رفتن همهچیز منجر شود.
راهکار ما:
- ثبت درخواست ممیزی
- مشاهده راهنمای چگونگی ثبت درخواست ممیزی در آپارات
- مشاهده راهنمای چگونگی ثبت درخواست ممیزی در یوتیوب
منابع
https://owasp.org/www-project-smart-contract-top-10/2025/en/src/SC05-reentrancy-attacks.html
https://swcregistry.io/docs/SWC-107/