In August 2021, one of the biggest cryptocurrency heists happened. Hackers stole $613 million worth of digital currency from a company named Poly Network. They exploited a vulnerability in the digital contract used by Poly Network. By exploiting this security weakness, they further damaged the organization’s smart contract security. And this eventually led to the loss of revenue and exposure of their customer data.
To prevent such situations, in this article, we’ll be discussing the functioning of smart contract security, its proper implementation, and the most commonly seen vulnerabilities of a smart contracts platform.
Here, we’ll help you gain better insights into the below topics:
Smart contracts are computer programs that execute the transactions automatically when predefined conditions are met. Smart contracts are self-executing contracts in which the contents of the buyer-seller agreement are inscribed directly into lines of code. They run on blockchain to enforce an agreement between the parties involved in the transaction. Their adoption in different sectors like healthcare, supply chain, finance has led to the strong demand for verification and validation techniques. The involvement of regulators and intermediaries is no longer necessary. Therefore, a smart contract needs to be highly secure with no bugs, loopholes, or vulnerabilities hidden in its programming codes.
A smart contract is a special kind of program that encodes business logic that runs on a special-purpose virtual machine baked into a blockchain or other type of distributed ledger.
The process of creating a smart contract starts with business teams working with developers to describe their requirements for the desired behavior of the smart contract in response to various events or circumstances. Simple events could be conditions such as payment authorized, shipment received, or a utility meter reading threshold. More sophisticated logic might encode more complex events such as calculating the value of a derivative financial instrument and processing trade of the derivative or automatically releasing an insurance payment in the event of a person’s death or a natural disaster.
Related article on Smart contracts in fintech: The possibilities
Security is one of the most important feature of a smart contract. So, knowing the weakest spots of a particular blockchain network can help developers make their contracts less susceptible to different vulnerabilities and attacks.
The main reason why there are vulnerabilities in smart contract security could be the impossibility of determining the state of the contract, in which the transaction will be executed, can cause vulnerability of the contract itself.
Related article on How to set up Ropsten testnet for your smart contract
There are many problems and vulnerabilities seen in smart contract security and here are some of the most prominent ones:
A reentrancy attack in a solidity smart contract can completely drain your smart contract of funds. When an external call is made to another untrusted contract a reentrancy attack occurs. The untrusted contract makes a recursive call back to the original function in an attempt to drain funds which can cause the called contract to use the intermediate state of the calling contract. This situation is not always known during development. A simple example is when a contract does internal accounting with a balance variable and exposes a withdrawal function. If the vulnerable contract transfers funds before it sets the balance to zero an attacker can recursively call the withdraw function repeatedly and drain the whole contract.
Blockhash function is similar to the timestamp dependency and it is not recommended to use it for the same reason as with the timestamp dependency because miners can manipulate these functions and change the withdrawal of funds in their analysis. This function is especially noticeable when block hash is used as a source of randomness.
Small discrepancies between the newly created token and actual ERC20 standard may lead to the non-functional method of the contract, and it will not be able to recognize the interface and thus leading to stuck funds and blocked contracts. There is a well-known OpenZeppelin implementation of the ERC-20 token, overused in modern protocols.
Frontrunning can be defined as overtaking an unconfirmed transaction and it is one of the hardest issues to prevent. It is a result of the blockchain’s transparency property. It is not easy to protect against frontrunning, and the issues detected in order to be fixed often requires some significant refactoring or redesign, All unconfirmed transactions are visible in the mempool before they are included in a block by a miner, and the interested parties can simply monitor transactions for their content and overtake them by paying higher transaction fees. This can be automated easily and has become quite common in decentralized finance applications.
This occurs when the nature of the system inhibits simultaneous operations when the operations should occur in a proper sequence. Smart contracts are executed only if certain conditions are met, there is a sequence to be followed. If external contracts are called in, it could inadvertently take over the control flow and make changes to the data. It was such a bug that caused the DAO’s collapse. There are different types of race conditions. Reentrancy race condition occurs when functions are called repeatedly before the first invocation of the function was finished. Cross function race conditions can occur when two different functions share the same state in a contract.
Before any transaction is added in a block by a miner, it is kept in the mempool for a short while. This makes it possible to tell what actions occur before its added to the block. But when it comes to decentralized markets, it goes a bit off; as the market order implemented is seen before the other transaction gets included in the block.
Contracts could have timestamp conditions to perform certain actions. The problem is when the miners manipulate them. The Blockchain accounts for the local system’s timestamp which causes a delay of seconds which is sufficient for hostile parties to launch an attack on the contract. This could affect the outcome of timestamp dependent contracts as the miner could then choose a different timestamp.
When a miner competes with a smartcontract participant to place their own information stored on the contract. When a participant places his bet, corresponding data is saved on the blockchain and any miner can access it to see the bet number simply by calling a public mapping. Since the condition of the called function isn’t updating until the end of mining, there is a risk of a reordering attack.
This kind of vulnerability is quite hard to happen. Anyhow, there is a possibility for it to happen. In this type of vulnerability, if the balance token reaches the maximum or minimum unit value, it will circle back to zero or the maximum set limit. If your token’s unit value is favorable for a user to reach the maximum limit of (2^256), then this vulnerability is a concern for you. But for most cases, it is not the case. Also, the ability of a user to update the unit value by calling a function makes the contract vulnerable to attack.
This vulnerability of ERC20 tokens was discovered by Golem team. A hacker can create an ethereum wallet address with trailing zeros and buy token from a contract by removing the trailing zeros. If the contract doesn’t check the length of the address of the sender, Ethereum’s virtual machine will just add zeroes to the transaction until the address is complete. This results in returning 256000 tokens for each purchase of 1000 tokens.
An example would be an auction contract. When a refund is attempted to the previous high bidder and it fails, then it reverts. This opens up a vulnerability when malicious actors could place themselves as the highest bidder by ensuring that all refunds to their address fail and this would prevent anyone else from bidding. It could also be the case with crowdfunding contracts which requires it to iterate through an array to pay users. If one payment fails, then the entire payout would revert.
A related problem as the previous point is if you are paying out to a lot of addresses at the same time, then you risk running out the block gas limit which is the maximum amount of computation that can be processed by a block. If that happens, it results in a failed transaction. An attacker could take advantage of this by adding a number of addresses that are due to very small payments. Thing is, the gas cost of these payments end up being more than the limit itself and that would block the refunds altogether.
The logic in the contract is designed to disallow payments to the contract unless it is forcibly done. There are quite a few methods to send Ether without activating the fall-back function. Thus, it is possible to make the balance of the contract greater than zero and possibly affect its function if is programmed with a condition which checks the balance in the contract.
Some of the above issues are more specific to smart contracts, others are common to all types of programming. By far the most common type of issue we detect consists of simple mistakes in the logic of the smart contract. These errors may be the result of a simple typo, a misunderstanding of the specification, or a larger programming mistake. These tend to have severe implications on the security and functionality of the smart contract.What they all have in common though, is the fact that they can only be detected if the auditor understands the code base completely and has an insight into the project’s intended functionality and the contract’s specification. It is these types of issues that are the reason smart contract audits take time, are not cheap, and require highly experienced auditors.
Since smart contracts are basically just very specific programs, the main goal of developers is to ensure the accuracy and security of their code. Here are some best practices for smart contract development.
The rich functionality of smart contracts is one of the reasons why platforms like Ethereum and EOS are so popular. This functionality, however, often comes at the price of a security, says our blockchain expert Mihail.
When working with protocols that support complex, multifunctional smart contracts, you must follow the best practices of the corresponding blockchain networks. Otherwise, you risk adding fatal vulnerabilities to your code.
Some networks, like Zilliqa and Cardano, help developers improve the security of their code by adding more restrictions to smart contracts. And while these restrictions may reduce contract functionality, the additional control improves contract security. In addition, when working with these networks, you can create automatic validation tools contracts to get a 100 percent guarantee of your smart contract safety.
One of the reasons why many blockchains invent their own programming languages is to reduce the number of possible bugs and errors in code. When working with popular programming languages, even experienced developers aren’t immune to making mistakes. There are just too many factors to keep in mind, including problems with the interaction between the language, the compiler, and the blockchain.
On the other hand, there are less complex programming languages that have simpler semantics, like the Scilla language that’s used for writing Zilliqa smart contracts. The simplicity of languages like Scilla makes it much easier for a developer to avoid programming mistakes when writing a contract.
Even though smart contracts can be classified as a type of software, they require development practices that take into account the specifics of blockchain technology. According to Mihail, the price of a development mistake in a blockchain in general (and in smart contracts in particular) is much higher than in other software solutions. In this field, you can’t just move fast and break things. Otherwise, you risk making lots of critical errors that could have been easily avoided.
For instance, the mechanisms of most blockchain networks allow calling the code of a contract in an unexpected way. Inexperienced developers may leave this issue out of a probability and write vulnerable code.
Just like with other software, you can’t foresee everything when developing a smart contract. That’s why running preliminary tests is a must.
Mihail suggests using all the testing options available, from using unit tests to cover basic contract functionality to releasing your smart contract on a test network first. This way, you can significantly increase your chances of finding critical errors in your code while you’re still able to fix them.
Since many blockchains use their own languages for writing smart contracts, you’ll have to use network-specific frameworks for each blockchain.
Security audits are another necessary measure for ensuring a high level of security for your smart contracts. This is the case firstly because professional auditors can not only detect possible flaws in your code but also give you some valuable advice on how to fix and optimize it.
Secondly, having a few people from outside your project take a look at the code gives you a fresh perspective. Consider hiring an independent penetration testing team and offering a bug bounty to make this process a bit faster.
Additional testing can’t harm your smart contract security. Since each blockchain network has its own set of specific tools, we’ll give you some examples of the most popular ones for Ethereum.
Here are tools that can help you improve the security of your Ethereum smart contracts:
The set of preferred tools for security audits and testing varies from blockchain to blockchain. Small and young smart contract platforms might not have any specific tools yet.
Security patterns and tools are the safety measures to mitigate damage and assure a reliable contract execution. Some of these smart contract security patterns and tools are listed below.
The Checks-Effects-Interaction pattern is a basic guideline while coding any smart contract. This pattern helps to know how to build up a function. It describes how the function code should be structured by avoiding side effects and undesired execution issues. It specifies a particular order of functions like checking all preconditions, making changes to the contract state, and eventually interacting with other contracts. Due to these functionalities, it is termed as a “Check-Effects-Interaction” pattern.
In this pattern, you need to validate all arguments and find out the errors when arguments are not applicable with expected input. Later, you can alter the states of smart contracts along with eventual interaction with other smart contracts. Interaction with other smart contracts should always be the final step in your function as it is associated with handing over the control to another contract. When handing over the control to another contract, you need to ensure that the current contract has completed its functionality and remains independent for execution on another contract.
Related article on How to set up Oraclize in your smart contracts
This pattern is well-known by the term emergency stop due to its capability of stopping operational execution in smart contracts. It is possible to manually trigger the circuit breaker with the trusted parties like contract admin or programmable sets to meet specific conditions. This pattern is highly used when bugs are detected.
Speed bumps are generally applied during the occurrence of malicious events. They provide the necessary time for smart contract owners to act accordingly. In DAO cases, speed bumps are useless as no recovery actions are possible. Hence, speed bumps are helpful in combination with circuit breakers that block the contract except for the withdrawal function. So, the users can easily withdraw the funds before the malicious action. For example, a large number of customers withdraw their funds simultaneously due to concerns about bank solvency. In such cases, banks counteract with some delaying, halting or limiting the functions of withdrawals.
This pattern helps regulate how frequently a function is called one after the other in a specified time interval. With this pattern, it is possible to limit the contract’s withdrawal execution rate by preventing the quick drainage of funds. Limiting the number of issued tokens over time at the contract level can be another effective way to use a rate-limiting pattern.
This pattern represents mutual exclusion, which is a synchronization mechanism for restricting concurrent access to resources. After reentrancy attacks, adopting mutex patterns can protect the smart contracts against recursive function calls from external contracts.
While coding smart contracts, it is better to manage the amount of money at risk. It is generally possible by limiting the overall balance available in the contract. This pattern keeps tracking the contract balance and rejects the payment when it exceeds the predefined limit.
Talk to our experts
Here are some of the security tools, which can reduce the possibilities of bugs in smart contract security.
While smart contract and blockchain technology is considered very secure and highly immutable, events such as the one at Poly Network can be sobering and give us pause. Exercising some caution would allow us to manage the risks associated with this technology and gain the most possible. Make sure your smart contract security prevents the above-mentioned vulnerabilities.
Talk to our experts to make sure your smartcontract is secure.