BNB Chain is one of the most popular blockchains in the Web3 world, and its reasonable fees, fast transactions, and rich project ecosystem have attracted a large number of users. Like any blockchain, developers on BNB Chain should consider security issues first during the development process: any loss of funds will lead to a decrease in user confidence in the protocol and platform, and security vulnerabilities and hacker attacks are one of the biggest risks faced by developers.
In this article, we provide ten practical security tips for developers to reduce risks and develop safer smart contracts on BNB Chain.
01
╱ Definition ╱
Replay attacks, also known as replay attacks or playback attacks, are a common type of attack in blockchain environments. In network security, a "replay attack" is a type of network attack in which valid data transmission is maliciously or fraudulently repeated or delayed.
In the context of Web3 and smart contracts, this usually means that an attacker is able to repeat transactions or actions in a smart contract without the permission of the original user. This can lead to various forms of fraud, such as double spending.
These attacks can have serious consequences for users and developers because they allow an attacker to reuse the same signature to gain unauthorized access to all funds or other assets on a smart contract.
To prevent replay attacks, developers must carefully design and implement their smart contract code and follow signature verification and industry best security standards.
02
╱ Case Analysis ╱
The following code snippet shows the implementation of a token transfer on the BNB chain. This code is vulnerable to a replay attack, which allows an attacker to reuse the same signature.
This feature lacks nonce or replay protection, allowing an attacker to "replay" a signed transfer transaction multiple times. An attacker can intercept a signed transaction and send it again to the same contract or another contract, and the contract will still consider it valid, so the attacker may exploit this vulnerability to steal assets.
03
╱ Improvement methods ╱
Add a nonce to the signature or use a mapping variable to record the signature. The more practical solution will vary depending on the project design.
01
╱ Definition ╱
A reentrancy attack occurs when a malicious contract repeatedly calls a vulnerable contract before the initial call has completed. In other words, an attacker can "trick" a vulnerable contract into thinking it has completed one transaction and is free to move on to the next, while in reality it is still executing the attacker's malicious code.
This could result in an attacker being able to manipulate the state of the contract in unexpected ways and potentially gain access to unauthorized funds.
02
╱ Case Analysis ╱
In the code snippet below, a user can withdraw funds from their account by calling the withdraw function and specifying the amount they want to withdraw. However, since the withdraw function does not protect against recursive calls to the function, it is vulnerable to reentrancy attacks.
An attacker can exploit the vulnerability by creating a malicious contract that calls the withdraw function multiple times before the balance is actually deducted from its account. The function msg.sender.call sends funds to the malicious contract, and the attacker repeatedly withdraws funds through the malicious contract's receive() function before its balance is reduced to zero, thus draining all funds from the victim contract.
03
╱ Improvement methods ╱
Make state updates before any external calls.
The pattern is called the Check-Effects-Interact pattern and is a design pattern used to prevent reentrancy attacks in smart contracts. The pattern separates state changes from external calls to other contracts by checking preconditions first and then updating the state before making any external calls. This way, if an external call triggers a callback that tries to call back into the contract, the state has already been updated, thus preventing other unexpected effects.
By following this pattern, developers can ensure that their contracts are more secure and less vulnerable to reentrancy attacks.
Another possible fix is to use a modifier to limit multiple calls to the same function, much like OpenZeppelin's ReentrancyGuard.
01
╱ Definition ╱
Oracles help smart contracts retrieve information from outside the blockchain. Usually the price of a decentralized exchange (DEX) asset is determined by an oracle extracting the price from the last successful transaction on the DEX.
However, the problem is that the price can be easily manipulated by anyone, causing problems with smart contracts. We often see cases of price oracles being manipulated through flash loans, which allow users to borrow huge amounts of money without collateral as long as they repay the loan within the same block. This makes it easy for attackers to influence or even manipulate prices and profit from false liquidations, excessive loans, or unfair trades.
02
╱ Case Analysis ╱
Below is a code snippet that is vulnerable to an oracle manipulation attack.
This contract allows users to swap token A for token B using the routing contract, but it relies on an external oracle (the Uniswap Pair contract) to obtain the reserves of token A and token B to calculate the price. The attacker was able to manipulate the reserves of the Uniswap Pair contract as well as the getAmountOut function, ultimately causing the swap to be executed at an unreasonable price.
03
╱ Improvement methods ╱
To prevent this attack, developers should use a decentralized oracle network that can obtain the volume-weighted average price (VWAP) or time-weighted average price (TWAP) of centralized and decentralized exchanges on-chain. This way, data will be collected from multiple data sources and over a wide range of time, making the code less susceptible to attacks and manipulation. It is important for developers to be able to remove any attack vectors for manipulating oracles in smart contracts to prevent potential vulnerabilities.
01
╱ Definition ╱
Correctly setting the visibility of functions ensures the security and integrity of smart contracts. Using incorrect function visibility settings can allow unintended users to manipulate the state of the contract, leading to serious problems such as stolen funds or control of contract functions.
By setting the visibility of a function to private or internal, developers can ensure that access to certain functions is limited and only authorized parties can call these functions. Private functions can only be called from the contract itself, while internal functions can also be called from the current contract. This allows developers to create more powerful and complex contracts while maintaining control over access to functions.
02
╱ Case Analysis ╱
The function setAdmin() allows anyone to set any address as a contract administrator. Depending on the permissions granted to the admin address within the contract, this could potentially cause the developer to lose control of the contract itself or even lose funds.
By setting the visibility of a function to internal , some contract functions may allow certain users to be set as administrators internally.
03
╱ Improvement methods ╱
Access modifiers are an important security feature that regulate who can access specific functions or variables in a contract. These modifiers can be used to limit the visibility of certain functions or variables to specific roles or addresses and prevent malicious actors from gaining unauthorized access or manipulating the contract state.
For example, a contract might have a function that only the contract owner can call, or a variable that can only be accessed by a specific set of addresses.
By changing the visibility modifier to external and setting the access modifier to onlyOwner, access to the setAdmin function can be restricted to the contract owner's address. This will prevent malicious external parties from taking control of certain privileged functions.
Proper use of visibility and restriction modifiers can make contract management easier and reduce common problems such as reentrancy attacks (attackers repeatedly call a function to manipulate the contract state) or front-running attacks (attackers monitor pending transactions and manipulate the contract state before legitimate transactions are executed).
By using these features appropriately, developers can enhance the security and reliability of their contracts, reduce the risk of unauthorized access, and improve the overall quality and maintainability of their code.
01
╱ Definition ╱
When deciding whether to upgrade a contract in the future, the design of the contract should be carefully considered at the beginning. Contract upgradeability refers to the property that the logic of a smart contract can be modified or updated after it is deployed on the blockchain. Although upgradeability can provide many advantages (such as fixing bugs, improving efficiency, or adding new features), it also introduces some risks. Contract upgrades may introduce new vulnerabilities, increase contract complexity, or cause unexpected consequences.
Contract upgradeability also raises trust issues, as proxy administrators can upgrade contracts without community consensus. Developers need to carefully weigh the pros and cons of upgradeability and determine whether it is really necessary for their project to introduce upgradeable contracts. In some cases, it is safer to design a contract that is intended to be immutable from the beginning rather than relying on the ability to modify it later.
02
╱ Improvement methods ╱
There are several important practices to follow when it comes to contract upgradeability. First and foremost, do not modify the proxy library. The Proxy contract library is extremely complex, especially in terms of storage management and upgrade mechanisms. Even small errors can greatly affect the operation of the Proxy and logic contracts. In fact, many of the serious proxy-related errors found during the audit were caused by improper modifications to the proxy library.
Another key practice for contract upgradeability is to include a storage "gap" in the base contract. Logic contracts must include a storage gap in the contract code to account for new variables that may be introduced when deploying new logic implementations. As new state variables are added, the size of the update "gap" becomes even more important. This practice ensures that future upgrades can proceed smoothly without issues.
Finally, it is important to avoid using selfdestruct() or performing delegatecall()/call() to untrusted contracts. Attackers can exploit these functions to subvert logic implementations or execute custom logic. To prevent this, it is important to validate user input! Do not allow contracts to perform delegatecall()/call() to untrusted contracts. Additionally, it is not recommended to use delegatecall() in logic contracts because it can be difficult to manage storage layout across multiple contracts. By following these practices, developers can minimize vulnerabilities and risks in their contract upgrades.
01
╱ Definition ╱
Front-running is a persistent and difficult-to-solve problem where users are able to take advantage of the delay between submitting a transaction and its confirmation on the blockchain. This delay is caused by the mempool.
The mempool is a temporary storage area for unconfirmed transactions that have been broadcast to the network. All nodes in the network maintain a mempool, allowing anyone to see pending transactions and potentially intercept them for profit. The mempool also provides an opportunity for miners to re-arrange transactions to maximize their profits, creating what is known as miner (or maximum) extractable value (MEV).
02
╱ Case Analysis ╱
Below is an example of an auction bidding feature that is prone to front-running.
This feature allows users to bid in auctions, but it can be subject to front-running attacks. Suppose a malicious user monitors the blockchain and sees that another user has submitted a high bid, the malicious user can quickly submit a higher bid and be processed first, eventually winning the auction.
In the following version, users submit bids that are not known to anyone and are stored in a mapping. Bid amounts are encrypted until the end of the bidding period.
03
╱ Improvement methods ╱
At the end of the bidding period, users can reveal their bids by submitting the original bid amount and a secret value. The contract verifies that the bid amount and the secret hash match the stored secret bid, ensuring that the bid was submitted before the end of the bidding period. If the bid is higher than the current high bid, it becomes the new high bid. By masking the bid amount until the end of the bidding period, this feature can mitigate front-running attacks.
Front running and MEV have become major concerns in the blockchain community, and various solutions, such as transactions and fair sorting services (FSS), have been proposed to address these issues. Transactions can help prevent front running by hiding transaction details from other users until the transaction is executed on the blockchain. On the other hand, FSS can reduce the impact of front running and MEV through secure off-chain transaction sorting.
Having a clear and comprehensive response plan is essential for handling emergency safety incidents. The plan should be reviewed, updated, and tested for effectiveness regularly. In the event of an emergency safety incident, time is of the essence. Therefore, the plan should be tailored in advance and include detailed operational steps such as identification, control, and mitigation.
The plan should be in place so that all stakeholders are aware of the situation. Regular backup of data is also important to prevent data loss. The plan should outline the recovery process to restore data and systems to a previous state. Team members should receive systematic training on the plan to ensure that everyone understands their roles and responsibilities.
A well-prepared response plan may not be able to fix the problem, but it can minimize the impact of the incident and maintain trust with users and stakeholders.
Regular code audits are essential to maintaining the security of your application. Working with a professional auditor who specializes in smart contract security is an important step in the development process. The auditor will check for vulnerabilities in the code and provide recommendations for improving overall security.
Prioritizing and resolving identified issues and maintaining open communication with auditors are critical to improving security.
In addition, communication with auditors is also critical. Auditors can explain in detail the problems and vulnerabilities they found and provide guidance and practical help on how to solve the vulnerabilities. By cooperating with professional audit institutions/personnel, security will be "taken to the next level."
For BNB Chain developers, conducting regular audits is an integral part of any comprehensive security strategy. Proactively identifying and addressing vulnerabilities in the code can minimize the risk of security breaches.
Using a bounty program is an effective way to incentivize the community of white hat hackers to report security vulnerabilities in project code. By offering incentives such as tokens or other rewards, any project can encourage experienced people to review the code and report any potential issues they find.
It is important to develop a clear and transparent bug bounty program. The plan needs to include: what types of vulnerabilities are eligible for rewards, how to evaluate the value of these vulnerabilities, etc. At the same time, adding a credible third party to the bug bounty program can help ensure the smooth operation of the program and the fair distribution of rewards.
It is also important to have a diverse group of bounty hunters. Different white hat hackers have different areas of expertise and can focus on finding problems that others may miss.
Finally, once vulnerabilities are reported, action must be taken quickly and effectively to resolve them. Bounty programs can be an effective tool for identifying vulnerabilities, but they only matter if the development team actually fixes the issue.
Educating Web3 users on security is a critical step in building a secure ecosystem. Keeping customers safe helps keep the platform secure. Users should be educated on best practices for protecting their accounts and sensitive information.
The most important part is learning how to avoid phishing scams.
Phishing scammers try to trick users into revealing their private keys or passwords by impersonating legitimate websites or services. CertiK recommends that users always double-check any emails, sources, etc., when receiving any information, and never enter private keys or passwords on untrusted websites.
Another important part is having a secure and strong password. CertiK recommends that users use unique and complex passwords for each account and avoid reusing passwords across different services. Also, use a password manager or other secure storage mechanism to store passwords securely.
Finally, it is extremely important to protect the private key. The private key is equivalent to the user's password and should be kept out of reach of others at all times. Users should avoid sharing their private keys with anyone and store them in a safe place.
Summarize
Developers building smart contracts and dApps on BNB Chain must take a comprehensive security approach to keep their users’ funds and assets safe. It is more important to be proactive rather than reactive when dealing with security vulnerabilities; to have a plan in place when or even before a vulnerability occurs, and to provide proper security education to all relevant users and stakeholders. By combining all of the above measures, projects can greatly reduce the risk of security vulnerabilities and hacker attacks.
