Top Ten DeFi Security Best Practices (Part 2)

Last week CertiK released Dry Goods Sharing | Top Ten DeFi Security Best Practices (Part 1), to share the first 3 practices of the 10 DeFi Security Best Practices, and today I will bring you the remaining 7 practices. Further help readers to protect the security of their own applications.

Super long dry goods, it is recommended to collect them first and then watch them!

4. Avoid common problems

This is an overarching issue for Solidity-if you want the contract to be secure, you need to verify all DeFi security guidelines in turn when building it. To write a truly reliable Solidity, you must understand its inner workings. Otherwise, you may be vulnerable to the following attacks:

Overflows/Underflows

In Solidity, uint256 and int256 are “wrapped”.

This means that if you have the largest number in uint256 and add it to it, it will become the smallest number possible.

This must be checked and verified. In Solidity versions before 0.8, a library like safemath[1] can be used to solve this problem.

In Solidity 0.8.x, arithmetic operations are checked by default. This means that x+y will throw an exception when it overflows. Therefore, please confirm the version of Solidity you are using.

Loops Gas Limit

When writing a dynamically sized loop, you need to pay attention to its limit size. The size of a cycle can easily exceed the maximum block limit and invalidate the contract when it rolls back.

Avoid using’tx.origin’

`tx.origin` may cause phishing-like attacks[2], so it should not be used for smart contract authentication.

Proxy Storage Collision 

For projects that adopt the agent implementation model, the implementation can be updated by changing the implementation contract address in the agent contract.

Usually, there is a specific variable in the proxy contract to store the address of the implementation contract. If the storage location of this variable is fixed, and there happens to be another variable with the same storage location index/offset in the execution contract, then a storage conflict will occur.

1640855695160544.png

To trigger a storage conflict, you can perform the following steps in Remix:

①Deploy and implement the contract;

②Deploy the proxy contract, and use the deployment address of the implementation contract as its construction parameter;

③Run the implementation contract on the deployment address of the agent contract;

④ Call the myAddress() function. It will return a non-zero address, which is the deployment address stored in the otherContractAddress variable in the proxy contract.

What happened in the above four steps?

1. Deploy the implementation contract and generate its deployment address;

2. The proxy contract is deployed using the deployment address of the implementation contract, where the constructor of the proxy contract is called, and the otherContractAddress variable is assigned to the deployment address of the implementation contract;

3. In step ③, the realization contract interacts with the proxy storage, that is, the variables in the deployed realization contract can read the value of the corresponding hash collision variable in the deployed proxy contract.

1640855666103399.jpg

4. The return value of the myAddress() function is the value of the myAddress variable in the deployed implementation contract, which conflicts with the otherContractAddress variable in the deployed proxy contract, and then the value of the otherContractAddress variable can be obtained there.

In order to avoid proxy storage conflicts, we recommend that developers implement unstructured storage proxies by choosing pseudo-random slots for storage variables.

A common practice is to adopt a reliable agency model for the project. The most widely adopted proxy mode is the Universal Upgradable Proxy Standard (UUPS) [3] and the transparent proxy mode [4]. They all provide a specific storage “offset” to avoid using the same storage slot in the agency contract and the implementation contract.

Below is an example of using transparent proxy mode to implement random storage?

1640855747376614.png

Ensure the accuracy of token transfer calculations

Normally, for ordinary ERC20 tokens, the number of tokens received should be equal to the original number of function calls.

For example-see below the function retrieveTokens()?

1640855757623752.png

However, if the tokens are deflationary, that is, each transfer has a fee, then the actual number of tokens received will be less than the number of tokens originally requested to be transferred.

In the modified function retrieveTokens(uint256 amount) shown below, `amount’ is recalculated based on the balance before and after the transfer operation. Regardless of the token transfer mechanism, this will accurately calculate the number of tokens transferred to’address(this)’.

1640855768495104.png

Delete data correctly

There are many scenarios where you need to remove an object or value that is no longer needed in the contract.

In a standard language like Java, there is a garbage collection mechanism that can automatically and safely deal with the problem of deleting data. However, in Solidity, developers must manually dispose of “garbage”. Therefore, improper garbage disposal may bring security issues to smart contracts.

For example, when using delete (ie `delete array[member]’) to delete a single member from an array, `array[member]’ will still exist, but reset to a default value based on the type of `array[member]’. Developers should either skip this member or reorganize the array and reduce its length.

for example?

1640855794827792.png

These are just some of the vulnerabilities that need to be paid attention to. Checking the auditor Sigma Prime’s article on common vulnerabilities in Solidity [5] can help you understand Solidity in depth and avoid these “pitfalls”.

5. Function visibility and modifiers

In the design of Solidity language, there are four types of function visibility:

  • private: This function is only visible in the current contract.
  • internal: The function is visible in the current contract and derived contracts.
  • external: This function is only visible to external calls.
  • public: This function is visible to both internal and external calls.

Visibility refers to one of the above four types of visibility for a specific function to restrict access to a certain group of users. 

Modifiers refer to custom code segments written specifically for access restriction purposes.

Visibility and modifiers can be combined to set appropriate access permissions for specific functions. For example, in the function `_mint()` implemented by ERC20:

1640855817125668.png

The visibility of the function _mint() is set to internal, which correctly protects it from being called externally. In order to set an appropriate access permission for the mint function, you can use the following code snippet:

1640855828675920.png

The function mint() only allows the owner of the contract to mint, and the require() statement can prevent the owner from minting too many tokens.

Correct use of visibility and modifiers is conducive to contract management. The incorrectly used settings may allow malicious attackers to call management configuration functions to manipulate the project. Excessive modifier settings may also cause centralization problems for the contract and cause anxiety in the community.

6. An external audit must be obtained before deployment to the mainnet

A code audit is like receiving a peer review with security as the focus. The auditor will review the entire code base line by line and use formal verification techniques to check whether there are any loopholes in the smart contract.

If you don’t want your project to be exposed to the threat of vulnerabilities naked, don’t deploy the code without audit, or change the code and redeploy after the audit.

Here are some suggestions to help ensure that the audit is comprehensive:

a. Record everything so that the auditor can track what happened more easily b. Keep the communication channel with the audit team open, in case they have any questions that can be resolved in time c. Add comments to your code to ensure that it can be updated Soon to be understood

However, safety is the most important thing you need to pay attention to. It shouldn’t exist in mind that all your net worth is entrusted to the auditing agency for your safety.

If your agreement is attacked, the biggest victims are yourself and your team.

Although a security audit is very useful, provides an additional round of review, and can help you find vulnerabilities that have not been discovered, it cannot ensure 100% security.

Tincho opened a topic on Twitter on how to perform security audits in the most efficient manner [6], and interested friends can check it out. Of course, if you have any audit needs or related questions, you can leave a message at the bottom of the official account for consultation at any time.

7. Test and use static analysis tools

You need to properly test the application.

If you are suffering from nowhere to start, Chainlink starter kit repos provides some test kit samples [7] for your reference.

Protocols like Aave and Synthetix also have very good test suites. It is recommended that you can also check their code to understand some of the best practices for testing (including more general coding).

Static analysis tools will also help you find errors and omissions in the code earlier. They can run automatically to monitor your contract and look for potential vulnerabilities.

One of the most popular static analysis tools is Slither [8]. CertiK is currently building next-generation static analysis, syntax analysis, vulnerability analysis, and formal verification tools based on its rich experience in auditing, verifying, and monitoring smart contracts.

8. Make safety a top priority

There is no doubt that you should do your best to create a safe and reliable smart contract before production deployment, but the rapid development of blockchain and DeFi protocols and the continuous emergence of new attack methods mean that this is far from enough to ensure security.

Developers should actively obtain and track the latest monitoring and alarm data, and try to introduce future-oriented features into the smart contract itself to access the rapidly growing dynamic security insights. This behavior has other benefits besides security.

The CertiK Skynet dynamic scanning system, as a 24*7 security intelligence engine, can provide multi-dimensional and real-time transparent security monitoring for the deployment of smart contracts on the chain.

It includes security primitives such as social sentiment, governance, market volatility, and security assessment, and provides comprehensive security insights for blockchain users, communities, and token investors.

The CertiK security rankings provide open, transparent, easy-to-understand security insights and the latest project status. Everyone can query all the required security data through this platform, and provide the platform with what they recognize as having excellent performance in the security field. The DeFi project, which will incentivize the formation of a community accountability system.

9. Develop a “turn over” plan

For an agreement, it is undoubtedly a good step if there is a long-prepared “turn over” plan after being attacked.

  • Purchase related insurance products
  • Set up an emergency “pause” function
  • There is an upgrade plan

With the continuous development of encryption technology, insurance platforms and plans are becoming more and more popular with projects and users as a guarantee to be rescued from losses, ensuring decentralization and increasing a certain degree of security. For example, CertiKShield, a decentralized on-chain fund insurance platform developed by CertiK, can guarantee the security of the project and its investors’ assets, and prevent them from being affected by security vulnerabilities or hacking caused by asset loss, theft or inaccessibility.

Setting up an emergency “pause” function is a strategy with pros and cons.

If a loophole is found, this function will stop all interactions with the smart contract. If you set up this feature, you need to make sure that your users know who can operate it.

If there is only one user, you are not running a decentralized protocol, then users can discover these through code. So pay attention to how this function is implemented, because you may actually end up with a centralized agreement on a decentralized platform.

The upgrade has the same problem. It may be good to move to a smart contract without bugs, but the upgrade still needs to be very cautious to avoid centralization problems.

Therefore, a considerable number of security agencies almost strongly oppose the scalable smart contract model.

For more information about smart contract upgrades, you can watch Patrick Collins’s video on this topic on YouTube [9] or check out the “Status of Smart Contract Upgrades” speech [10].

Want more insurance advice? Welcome to join Chainlink Discord:

https://discord.gg/2YHSAey

10. Prevent pre-running transactions

In the blockchain, all transactions are visible in mempool[11], which means that everyone has the opportunity to see your transaction, and it is possible to make a transaction before your transaction, in order to obtain information from your Profit from trading.

For example, suppose you use DEX to exchange 5 ETH into DAI at the current market price. Once you send your transaction to mempool for processing, a forerunner can trade before you and buy a large amount of ETH, causing the price to rise. Then they can sell you the ETH they bought at a higher price and make a profit with the difference.

At present, preemptive trading robots [12] are acting recklessly in the blockchain world, making profits at the expense of ordinary users.

This term comes from traditional finance. Traders use the same operation to make profits, involving financial assets and tools such as stocks, commodities, derivatives, etc.

As another example, the functions listed below are at risk of being preempted.

According to the modifier’initializer’, this function can only be called once.

If the attacker monitors the transaction calling the `initialize()` function in the mempool, then the attacker can use a set of customized tokens, distributor, and factory values ​​to `copy` the transaction, And ultimately control the entire contract. Since the function “initialize()” can only be called once, the contract owner cannot defend against such attacks.

1640855858095574.png

This is usually also related to miner extractable value or MEV [13]. MEV refers to the reordering of transactions by miners or robots so that they can profit from the ordering in some way.

Just as early players pay more gas to make their transactions ahead of yours, miners can directly reorder transactions to make their transactions ahead of yours. In the entire blockchain ecosystem, MEV steals millions of dollars in assets from ordinary users every day.

Fortunately, a group of world-class smart contract and cryptography researchers, including Ari Juels, the chief scientist of Chainlink Labs, are working to solve this problem with a solution called “Fair Sorting Service”.

The solution under development-Chainlink Fair Sequencing Service (FSS)

The Chainlink 2.0 white paper [14] outlines the main features of the fair ordering service, which is a secure off-chain service powered by the Chainlink decentralized oracle network (DONs), which will be used to order transactions according to the fair time concept outlined by DApps .

FSS aims to greatly alleviate the negative impact of preemptive transactions and MEV, and reduce the consumption of fees for users of the entire blockchain ecosystem.

More information on this can be found in an introductory blog post [15] or in Section 5 of the Chainlink 2.0 white paper.

In addition to FSS, one of the best ways to alleviate the preemptive transaction problem is to reduce the importance of transaction ordering as much as possible, thereby inhibiting the reordering of transactions and MEV in the agreement.

Write at the end

The crypto world is a fair and transparent place that requires everyone of us to collaborate and help each other, and this is especially true for developers.

At this point of protecting yourself, reviewing the attacks that have taken place helps to understand how malicious attackers carry out attacks. In addition, you can also receive the latest information about security vulnerabilities on the chain and their update status in real time through a 7*24 all-weather security insight system (such as CertiK Skynet). 

If you want to learn more about security and are afraid that the process is too boring, it is recommended to take a look at the Ethernaut game [16]. You can use it to understand the security issues in DeFi, it contains a large number of DeFi security examples, as well as many connotations and extensions of Solidity.

And Damn Vulnerable DeFi[17] can also learn related safety knowledge in an entertaining way.

If you want to learn more about flash loan attacks, you can log in to the Prevent Flash Loan Attacks website[18].

For everyone in the crypto world, learning from mistakes and lessons learned is the most basic, which can ensure that our smart contracts are safely constructed and adopted as widely as possible.

If there is more information you want to know about the solution in this article, please refer to the Chainlink document [19] and CertiK document [20].

Posted by:CoinYuppie,Reprinted with attribution to:https://coinyuppie.com/top-ten-defi-security-best-practices-part-2/
Coinyuppie is an open information publishing platform, all information provided is not related to the views and positions of coinyuppie, and does not constitute any investment and financial advice. Users are expected to carefully screen and prevent risks.

Like (0)
Donate Buy me a coffee Buy me a coffee
Previous 2021-12-31 08:31
Next 2021-12-31 08:34

Related articles