How To Secure Your Smart Contracts
Photo by chris panas on Unsplash

How To Secure Your Smart Contracts

By alexroan | Blockchain Developer | 26 Apr 2020


This article explains how to secure your smart contracts against reentrancy and ownership theft attacks.

Prerequisites: A basic understanding of the Ethereum Blockchain and Smart Contracts.


Introduction

Tunnel vision, a pitfall that every developer has experienced. When developing new code for a specific purpose, it’s easy to become so focussed on solving a particular problem that we miss something important.

This is especially true when transitioning from one technology to another. For example: If you come from a background in javascript, it’s unlikely you’d have much concern for overflow exploitation, yet in Solidity, it needs to be addressed.

We’re going to go through some weaknesses that are inherent in Solidity: Reentrancy Hacks and Owner Logic Theft.


Reentrancy Attacks

What

Smart contracts will often need to call or send ether to an external address. This type of operation is inherently vulnerable to reentrancy.

To perform a reentrancy attack, the attacker deploys a malicious contract to the network. This contract intends to manipulate the logic of the target contract into sending Ether to it, thus invoking its fallback function. The fallback function then recalls the target contract during the execution, to drain the target contract of Ether.

Photo by Bret Kavanaugh on Unsplash

How

Take the following function inside a vulnerable target contract:

function withdraw() external {
uint amount = balances[msg.sender];
require(msg.sender.call.value(amount)());
balances[msg.sender] = 0;
}

Assuming balances is a mapping of addresses to integer values, the function performs the following actions:

  • Retrieve the amount of Ether to send to the caller.
  • Send that amount to the caller
  • Set the balance of the caller to be zero

So long as the withdraw function is being called by a non-contract address, the logic is fine. However, if the sender is a contract address, this function can be exploited to drain the contract.

Here is a simple fallback function within our malicious contract:

function() external payable {
while(calls < 10){
calls++;
reentrancyContract.withdraw();
}
}

Assume that calls is a state variable defined as 0 in the constructor. When the target contract attempts to send Ether to the malicious address where this contract resides, this fallback function is called. If calls is less than 10, the withdraw() function on the target contract will be called again. This repeats recursively until it is called 10 times, draining the target contract of 10 times more than intended.

This happened in real life. In 2016, the DAO hack caused huge shockwaves throughout Ethereum and the Blockchain industry. Its consequences are still being felt today.

Preventing Reentrancy Attacks

There are several ways to protect your contracts from reentrancy attack.

The first is to use transfer() to send Ether. In previous versions of Solidity, contracts needed to use the call() method, in which no gas limit was set by default. transfer() on the other hand currently has a limit of 2,300 gas units, which is about enough to emit an Event. With that limit, any complex recursive execution would run out of gas almost immediately.

Use the Checks→Effects→Interactions pattern. When writing code for sensitive operations, make sure you initially:

  1. Run checks: require() statements.
  2. Then make the changes that affect state variables: balance -= withdrawAmount;.
  3. Then finally, perform the interaction: address.transfer(withdrawAmount).

Another technique is to lock the contract, also known as a mutex. Use a state variable to determine if a contract function is currently being executed. If the code is locked, no function can be called until the lock is released.


Owner Logic Theft

What

Function modifiers allow developers to identify who can call each function in a contract depending on their privilege. Where the keywords public and private are common to developers of other languages, Solidity also allows for custom modifiers.

A common modifier pattern is onlyOwner. This is where a state variable owner records the address which deployed the contract. Any functions with this modifier require that the caller address be equal to the owner.

The custom modifier looks like this:

modifier onlyOwner {
require(msg.sender == owner);
_;
}

Functions which use this modifier can be declared like this:

function somethingImportant() public onlyOwner { … }

Becoming the owner of a contract is powerful, as the owner usually has the highest privileges to interact with the contract.

Photo by Shane Avery on Unsplash

How

OnlyOwner contracts will often have a function to change the owner:

function setOwner(address _newOwner) public {
owner = _newOwner;
}

The problem here is that this function is public, meaning anyone can call this function and therefore become the owner of the contract. An additional modifier of onlyOwner should be applied to the function declaration.

Preventing Owner Theft

Follow good practices and always use the correct modifiers for functions. This goes without saying, but as mentioned earlier, tunnel vision can sometimes contribute to letting issues like these slip through the net.

Get a second opinion. There are plenty of communities and companies that perform smart contract audits. If you’re deploying sensitive contracts, I highly recommend that you utilise these services.

Use industry-standard libraries. OpenZeppellin has a suite of libraries and contracts which are thoroughly audited by the community before being released.


Conclusion

Reentrancy is the most high profile exploit because of the DAO hack. Steps have been taken to mitigate the risk, as the introduction of transfer() , but you still need to be wary of it and code accordingly.

Always use best practices, industry standards and libraries accepted by the community. Inheriting contracts scrutinized by many developers means you can have more confidence that they work as designed.

Use static analysis tools and audit your code.

Read part 2 which discusses Arithmetic Under and Overflow vulnerabilities and how to mitigate them.


Learn More

If you’re interested in Blockchain Development, I write tutorials, walkthroughs, hints, and tips on how to get started and build a portfolio. Check out this evolving list of Blockchain Development Resources.

If you enjoyed this post and want to learn more about Smart Contract Security, Blockchain Development or the Blockchain Space in general, I highly recommend signing up to the Blockgeeks platform. They have courses on a wide range of topics in the industry, from Coding to Marketing to Trading. It has proven to be an invaluable tool for my development in the Blockchain space.

How do you rate this article?


54

0

alexroan
alexroan

Blockchain Developer


Blockchain Developer
Blockchain Developer

Tutorials, walkthrough, hints and tips on Blockchain Development for all levels of expertise.

Send a $0.01 microtip in crypto to the author, and earn yourself as you read!

20% to author / 80% to me.
We pay the tips from our rewards pool.