آنچه در این مقاله می‌خوانید [پنهان‌سازی]

Reentrancy یک نوع آسیب‌ پذیری امنیتی است که می‌تواند به قراردادهای هوشمند نوشته‌شده در زبان سالیدیتی آسیب بزند. این آسیب‌پذیری زمانی رخ می‌دهد که یک قرارداد در حال اجرای یک تابع به یک قرارداد دیگر فراخوانی می‌شود و آن قرارداد می‌تواند دوباره به تابعی (همان تابع یا تابعی دیگر) در قرارداد اصلی برگردد و وضعیت داخلی آن را تغییر دهد. در این مقاله، به بررسی reentrancy در سالیدیتی، خطرات آن و روش‌های پیشگیری از این آسیب‌پذیری خواهیم پرداخت.

مفهوم Reentrancy

Reentrancy در سالیدیتی به این معنی است که یک قرارداد می‌تواند به طور همزمان چندین بار از یک تابع خاص فراخوانی شود. این مشکل به خصوص در توابعی که دارای توکن‌ها یا مقادیر مالی هستند بسیار خطرناک است.

مثال زیر را فرض کنید:

contract Vulnerable {
    mapping(address => uint) public balances;

    function withdraw(uint _amount) public {
        require(balances[msg.sender] >= _amount);
        balances[msg.sender] -= _amount;
        msg.sender.call.value(_amount)("");
    }
}

در این مثال، وقتی که کاربری withdraw را فراخوانی می‌کند، ابتدا saldo (مقدار) آن کاربر کاهش می‌یابد و سپس مبلغ به او ارسال می‌شود. اگر در حین این عملیات یک فراخوانی دوباره (Reentrancy) به تابع withdraw صورت گیرد، کاربر می‌تواند بدون کاهش واقعی مقدار خود، چندین بار پول برداشت کند.

آموزش سالیدیتی (به همراه 5 پروژه عملی)

خطرات Reentrancy

خطر اصلی Reentrancy این است که می‌تواند منجر به برداشت‌های غیرمجاز و از دست رفتن وجوه قرارداد شود. بعضی از بزرگترین سرقت‌های بلاکچین به دلیل این آسیب‌پذیری بوده است، که منجربه از بین رفتن سرمایه‌های زیادی شده‌اند.

روش‌ های پیشگیری از Reentrancy

  1. استفاده از الگوی “قفل”: با استفاده از یک متغیر قفل، می‌توان از فراخوانی‌های همزمان جلوگیری کرد. به طور مثال:

    contract Safe {
        bool internal locked;
    
        modifier noReentrancy() {
            require(!locked, "No reentrancy");
            locked = true;
            _;
            locked = false;
        }
    
        function withdraw(uint _amount) public noReentrancy {
            // Logic here
        }
    }
    
  2. استفاده از “Request-Response”: به جای ارسال وجوه در یک تابع، می‌توانید یک “درخواست” برای برداشت ایجاد کنید و سپس کاربر باید با یک تابع دیگر آن درخواست را تأیید کند.

  3. بررسی و تأیید مقادیر قبل از ارسال: همیشه باید اطمینان حاصل شود که مقدار موجود در حساب کاربر قبل از هرگونه تغییر، بررسی و تأیید شده باشد.

نتیجه‌ گیری

reentrancy در سالیدیتییکی از بزرگترین چالش‌ها در امنیت قراردادهای هوشمند است. آگاهی از این آسیب‌پذیری و به کارگیری روش‌های پیشگیری می‌تواند به طور قابل توجهی خطرات مرتبط با آن را کاهش دهد. توسعه‌دهندگان باید همیشه به دنبال نوشتن کدهای امن و انجام تست‌های جامع برای شناسایی و اصلاح این نوع آسیب‌پذیری‌ها باشند.