How to audit Smart Contract
Blockchain technology presents a promising opportunity for businesses due to its benefits like transparency, decentralization, security and more. Many blockchain platforms, including EOS and Ethereum, utilize smart contracts that contain the business logic of the application that allows two or more parties to perform transactions in a trusted and secure way.
The widespread adoption of smart contracts demands high-security standards. Smart contracts applications have resulted in the loss of millions of USD in history, for example, DAO attack.
Therefore, it is essential to perform smart contracts audit to mitigate security risks. In this article, we will discuss various smart contract attacks and the smart contract auditing process.
Smart Contract Audit Process is quite similar to that of a regular code audit which is performed to detect security vulnerabilities and flaws before the code is deployed on the public network.
Since the blockchain is an append-only and a replicated linked-list of Merkle trees while the smart contracts are self-executing, it is essential to find risks in the code before launching it.
Let’s discuss some of the smart contract attacks that may lead to millions of USDs losses.
Mentioned below are some of the smart contract attacks:
This attack occurs when the attacker takes away funds from the target by calling the target’s withdraw function recursively and the same happened in the case of DAO attack.
In case the contract is not able to update its state before sending funds, the hacker can call the withdraw function continuously to get the contract’s funds.
Every time an attacker receives Ethers, the attacker’s contract calls its fallback function automatically, which is written to call the withdraw function. The attack enters a recursive loop at this moment and the contract’s funds start to drain to the attacker.
As the target contract gets baffled while calling the attacker’s fallback function, the contract can never update the attacker’s balance.
Reentrancy attacks occur because of two specific smart contract vulnerabilities. The first is when a contract’s state is updated after funds are sent. Due to the inability to update the contract state before sending funds, the function can be interrupted in the mid of computation and the contract can be deceived into assuming that funds have not been sent.
The second smart contract vulnerability is when the function address.call.value () is used incorrectly to send funds as opposed to address.send () or address.transfer ().
Therefore, to avoid reentrancy attack, update the contract’s balance before sending funds and always use address.send () or address.transfer () while sending funds.
2. Cross-Function Race Condition
As mentioned in the Reentrancy attack section, the inability to correctly update the contract state resulted in the stolen funds. This issue, combined with external calls is the potential for a cross-function race condition.
Since all transactions in the Ethereum can run one after another, external calls can become a disaster if not handled properly. A cross-function race condition occurs when two functions are called and have the same state. The contract seems like two contract states exist, but in fact, only one true state exists.
Finish all internal work first before you call external functions and avoid making external calls. Ensure to use a mutex when it is impossible to prevent external calls. When external calls seem unavoidable, make external call functions as “untrusted.”
You should remember that uint8 can only take values between 0 to 255. The Ethereum Virtual Machine was designed to use 256 bits as the number of bits processed by the CPU in one go. If someone goes over this range, the figure needs to be reset to the bottom range, i.e., ²²⁵⁶ + 1 = 0.
Underflow condition occurs when we subtract a number greater than zero from zero, leading to a newly assigned integer of ²²⁵⁶. If the attacker’s balance comes across underflow error, the balance is updated in a way that all funds could be stolen.
To avoid the issue of underflow condition, check if the updated integer is within the byte range. You can add a parameter check in your code that might act as a last line of defense. The first line of function withdraw () looks for adequate funds while the second line detects overflow and third checks for underflow condition.
Here are some of the necessary checks that should be undertaken with any smart contract:
Correct visibility of functions:
Solidity’s functions have four types of visibility specifiers: external, public, private or internal with the public being the default. On the other hand, state variables can be internal or private, public with internal being the default. Specifying the state variables and visibility of functions is the best security practice.
Absent specifiers can be ineffective in the case of functions where public access is the default. Therefore, ensuring correct visibility of functions is essential for keeping the smart contracts secure.
Prevent overflow and underflow:
When a number gets increased above its maximum value, it is defined as an overflow. Solidity can manage up to 256-bit numbers, therefore incrementing by 1 would lead to the result as 0.
In the other case, when the number is unsigned, decrementing the value can underflow the number and result in the maximum possible value.
Using SafeMath library to perform math operations in smart contracts can prevent the issues of underflow and overflow.
Data can be stored in non-persistent and less expensive memory or in a storage that is highly expensive and persistent in Solidity. You should carefully analyze where the data should be stored while writing smart contracts. Local variables and state variables are kept in storage, while function parameters are stored in memory by default.
Check for reenterancy and ensure state committed before the external call:
The reenterancy attack is one of the famous Ethereum vulnerabilities and it was unveiled during a multi-million dollar heist that resulted in a hard fork of Ethereum.
The attack occurs when external calls make new calls to the calling contract before the completion of initial execution. The contract state can change in the middle of execution because of the call to an untrusted contract.
Save gas on smart contracts:
Saving gas is essential for building an efficient smart contract. Since not all developers know how to save the gas efficiently, it is one of the significant issues faced by the developers. It is essential to understand how to minimize the gas or to avoid it.
Compiler warnings are serious concerns that developers sometimes ignore and deploy contracts without considering them. It is recommended to take necessary action to be taken to eliminate all these warnings.
Smart Contract Audit Tools
A thorough and comprehensive audit can include test alongside use cases and documentation. While auditing the smart contracts, one should also consider behavior driven development practices but with an emphasis on security instead of functionality.
Truffle can be used for auditing Ethereum smart contracts. Use the standard npm install -g truffle for installing the framework and truffle init for creating the project structure.
Another alternative is Mythril that can be used for finding uint underflows and overflows. Etherscrape tool is used to scrape live Ethereum contracts for reentrancy bugs when send () function is used.
The above-mentioned strategies and tools can help you enhance the quality of smart contracts in terms of both security and functionality.
At LeewayHertz, our team of smart contracts developers considers auditing of smart contracts essential and ensure to test contracts carefully before launching them on the public network.