S-20200129

Security Intermediate January 29, 2020

Back to All Tasks

Problem Statement

You are tasked with developing a smart contract for an Ethereum-based token exchange platform. The contract should allow users to deposit and withdraw Ether, as well as trade tokens. However, during the development phase, you discover that the contract is vulnerable to reentrancy attacks. Your job is to identify the vulnerability in the provided code snippet and refactor it to prevent such attacks. Consider using the Checks-Effects-Interactions pattern or locks to ensure the security of the smart contract.

Concepts

  • Smart Contract Vulnerabilities
  • Reentrancy Attack

Constraints

  • Use Solidity version ^0.8.0
  • Ensure that the refactored contract passes all existing tests without modification

Security Notes

  • Always validate external calls and consider potential reentrancy risks
  • Implementing proper access control mechanisms can help mitigate security vulnerabilities

Solutions

Php Solution

pragma solidity ^0.8.0;

contract SafeTokenExchange {
    mapping(address => uint256) public balances;
    bool private locked;

    modifier noReentrancy() {
        require(!locked, "No re-entrancy");
        locked = true;
        _;
        locked = false;
    }

    // Function to deposit Ether into the contract.
    function depositEther() public payable {
        balances[msg.sender] += msg.value;
    }

    // Function to withdraw Ether from the contract.
    // Uses noReentrancy modifier to prevent reentrancy attacks.
    function withdrawEther(uint256 _amount) public noReentrancy {
        require(balances[msg.sender] >= _amount, "Insufficient balance");

        balances[msg.sender] -= _amount;  // Checks-Effects-Interactions pattern: effect comes before interaction
        payable(msg.sender).transfer(_amount);
    }

    // Function to trade tokens.
    function tradeTokens(address _recipient, uint256 _amount) public {
        require(balances[msg.sender] >= _amount, "Insufficient balance");

        balances[msg.sender] -= _amount;
        balances[_recipient] += _amount;
    }
}

This Solidity smart contract implements a secure token exchange platform where users can deposit and withdraw Ether as well as trade tokens. The primary security concern addressed in this contract is the reentrancy attack, which allows an attacker to repeatedly call the withdrawal function before their funds are actually deducted from their balance.

To mitigate this risk, we use the Checks-Effects-Interactions pattern and a noReentrancy modifier. In the withdrawEther function, we first check if the user has enough Ether (check), then update the balances mapping (effect), and finally transfer the Ether to the user (interaction). This ensures that any malicious reentrant calls can only occur after the balance is already updated.

The noReentrancy modifier introduces a boolean flag 'locked' which is set to true before executing critical sections of code (where external calls are made) and reset to false afterwards. This prevents reentrancy attacks by ensuring that these sections cannot be executed recursively.

By applying these security best practices, the contract is protected against common vulnerabilities while maintaining functionality.