Cointime

Download App
iOS & Android

Top 10 Security Tips for BNB Chain Builders

BNB Chain is one of the most in-demand blockchains in the Web3 world thanks to its low fees, fast transactions, and rich ecosystem of projects. As with any blockchain, it's important for builders on BNB Chain to prioritize security in their development process, as any loss of user funds leads to the erosion of confidence in protocols and platforms. Security breaches and hacks are among the biggest risks developers face. In this post, we provide our top 10 security tips on for developers can reduce risk and develop secure smart contracts on BNB Chain.

#1: Protect Against Replay Attacks

Replay attacks are a common type of attack in blockchain environments that exploit vulnerabilities in signature verification processes. These attacks can have serious consequences for both users and developers, as they allow attackers to repeatedly use the same signature to gain unauthorized access to funds or other assets held by a smart contract.

To prevent replay attacks, developers must carefully design and implement their smart contract code, and to follow industry-standard best practices for signature verification and security.

This code snippet represents a basic implementation of a transfer function for a token on the BNB chain which is vulnerable to replay attacks, which would enable an attacker to repeatedly use the same signature.

function transfer(address to, uint256 value, bytes calldata signature) public returns (bool) {
    require(value <= balanceOf[msg.sender], "Insufficient balance.");
    require(_verifySignature(msg.sender, to, value, signature), "Invalid signature.");
    balanceOf[msg.sender] -= value;
    balanceOf[to] += value;
    emit Transfer(msg.sender, to, value);
    return true;
}

function _verifySignature(address from, address to, uint256 value, bytes memory signature) internal view returns (bool) {
    bytes32 messageHash = keccak256(abi.encodePacked(from, to, value));
    bytes32 ethSignedMessageHash = keccak256(abi.encodePacked("\x19Binance Signed Message:\n32", messageHash));
    address recoveredAddress = ECDSA.recover(ethSignedMessageHash, signature);
    return recoveredAddress == from;
}

This function lacks nonce or replay protection, allowing an attacker to replay a signed transfer transaction multiple times. An attacker can intercept the signed transaction and send it again to the same contract or another contract, and it will still be considered valid by the contract. This could lead to the attacker stealing the protocol’s assets. Remediations include adding a nonce in the signature or using a mapping to record the use of a signature. The exact solution may rely on the design of the project and can be modified accordingly to match the needs of specific contracts.

#2: Beware of Reentrancy Attacks

A reentrancy attack occurs when a malicious contract repeatedly calls back into a vulnerable contract before the initial invocation is complete. In other words, the attacker tricks the vulnerable contract into thinking that it is done with a transaction and is free to move on to the next one, when in reality it is still executing the attacker's malicious code. This can result in the attacker being able to manipulate the state of the contract in unexpected ways and potentially gain access to unauthorized funds.

In the following code snippet, users can withdraw funds from their account by calling the withdraw function and specifying the amount they want to withdraw. However, the withdraw function is vulnerable to a reentrancy attack because it does not properly prevent recursive calls to the function.

mapping (address => uint) public balances;

function withdraw(uint _amount) public {
    require(balances[msg.sender] >= _amount);
    (bool success, ) = msg.sender.call.value(_amount)("");
    if (success) {
        balances[msg.sender] -= _amount;
    }
}

An attacker can exploit this vulnerability by creating a malicious contract that calls the withdraw function multiple times before the balance is actually deducted from their account. The function msg.sender.call sends funds to the malicious contract where the the attacker repeatedly withdraws funds, through the malicious contract receive() function before their balance is reduced to zero, effectively draining the victim contract of all its funds.

contract MaliciousContract {
    receive() external payable {
        VictimContract.withdraw();
    }
}

A simple fix would be to include a status update before any external calls.

mapping (address => uint) public balances;

function withdraw(uint _amount) public {
    require(balances[msg.sender] >= _amount);
    balances[msg.sender] -= _amount;
    (bool success, ) = msg.sender.call.value(_amount)("");
    require(success, "Transfer failed.");
}

This is called the Check-Effects-Interact pattern, which is a design pattern used to prevent reentrancy attacks in smart contracts. It involves separating the state changes from the external calls to other contracts by first checking the preconditions and then updating the state before making any external calls. This way, if an external call triggers a callback that attempts to call back into the contract, the state has already been updated, preventing any unintended 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 make use of a modifier to restrict multiple calls to the same function, much like OpenZeppelin’s ReentrancyGuard implementation.

#3: Be Careful With Oracles

An oracle helps smart contracts retrieve information from outside the blockchain. Often on a decentralized exchange (DEX), the price of assets is determined by an oracle mechanism that pulls the price from the last successful trade on the DEX. The problem is that this price can be manipulated easily by anyone, causing the smart contract to execute in unexpected ways. This manipulation can happen through the use of a flash loan, which allows a user to borrow huge amounts of funds without any collateral, as long as the loan is repaid within the same block. Since the price oracle in question does not protect against this type of manipulation, attackers can easily influence the price to profit from false liquidations, excessive loans, or unfair trades. This type of attack is called an "oracle manipulation attack."

The following is an example of code that is vulnerable to an oracle manipulation attack.

function getAmountOut(uint _amountIn) public returns (uint) {
    (uint reserveA, uint reserveB, ) = IUniswapV2Pair(router.WETH()).getReserves();
    uint amountOut = router.getAmountOut(_amountIn, reserveA, reserveB);
    return amountOut;
}

This contract allows users to swap Token A for Token B using the Uniswap router, but it relies on an external oracle (the Uniswap pair contract) to get the reserves of Token A and Token B in order to calculate the price. An attacker is able to manipulate the reserves of the Uniswap pair contract and can manipulate the getAmountOut function, causing the swap to be executed at an unfavorable price.

To prevent this type of attack, developers should instead make use of decentralized oracle networks that can be used to get volume-weighted average prices (VWAP) or time-weighted average prices (TWAP) on-chain for centralized and decentralized exchanges. This way, the data feeds will come from multiple data sources and price time frames, which will make the code much less susceptible to attacks and manipulation. It is important for developers to remove any oracle manipulation attack vectors in their smart contracts to prevent potential exploits.

#4: Set Proper Function Visibility Settings and Access Rights

It is important to set the visibility of functions correctly to ensure the security and integrity of the smart contract. The use of incorrect function visibility settings can cause serious security vulnerabilities, as it allows unintended users to manipulate the contract state and potentially steal funds or take control over important contract functions.

By setting the visibility of functions to private or internal, developers can restrict access to certain functions and ensure that only authorized parties can execute them. Private functions can only be called from within the contract itself, while internal functions can also be called from within contracts that inherit from the current contract. This allows developers to create more complex contracts with greater functionality, while still maintaining control over who can access certain functions.

Consider the function setAdmin() which allows anyone to set any address as a contract admin. Depending on the privileges granted to the admin address within the contract, this could potentially lead to lost funds and loss of control over the contract itself.

function setAdmin(address account) external {
    admin[account] = true;
}

By setting the function visibility to internal, this can allow only certain contract functions to internally set certain users as admins.

function setAdmin(address account) internal {
    admin[account] = true;
}

Access modifiers are an important security feature that can be used to dictate who has access to specific functions or variables within the contract. These modifiers are used to restrict the visibility of certain functions or variables to specific roles or addresses, preventing unauthorized access or manipulation of the contract's state by malicious actors. For example, a contract may 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 leaving the visibility modifier to external and setting the the access modifier to onlyOwner, access to the setAdmin function will be restricted to only the contract owner address. This will prevent malicious external parties from taking control of certain privileged functions.

function setAdmin(address account) external onlyOwner {
    admin[account] = true;
}

Proper use of visibility and restriction modifiers can make contract management much easier. Common attacks such as reentrancy attacks, where an attacker repeatedly calls a function to manipulate the contract state, or front-running attacks, where an attacker monitors pending transactions and manipulates the contract state before a legitimate transaction is executed, can be reduced as well. By using these features appropriately, developers can enhance the security and reliability of their contracts, reduce the risk of unwanted changes or unauthorized access, and improve the overall quality and maintainability of their code.

#5: Beware of Contract Upgradability Issues

Carefully consider the contract design when deciding whether to include contract upgradability. Contract upgradability refers to the ability to modify or update the logic of a smart contract after it has been deployed on the blockchain. While upgradability can offer many advantages, such as fixing bugs, improving efficiency, or adding new features, it also introduces some risks, such as introducing new vulnerabilities, increasing complexity, or causing unintended consequences. The fact that contracts can be upgraded also raises trust issues because the proxy admin can upgrade the contract without consensus from the community. Therefore, it is important to carefully weigh the benefits and drawbacks of upgradability and determine whether it is truly necessary for a given use case. In some cases, it may be more appropriate to design a contract that is intended to be immutable and secure from the outset, rather than relying on the ability to modify it later.

When it comes to contract upgradability, there are several important practices to follow. First and foremost, it is crucial not to modify the proxy library. The complexity of proxy contract libraries, particularly with regards to storage management and upgrade mechanisms, means that even minor mistakes in modification can significantly impact the functioning of the proxy and logic contract. In fact, many high-severity proxy-related bugs discovered during audits have been caused by improperly modified proxy libraries.

Another key practice for contract upgradability is to include a storage gap in base contracts. Logic contracts must have a storage gap built into the contract code to account for new state variables that may be introduced when a new logic implementation is deployed. It is important to update the size of the gap accordingly after adding new state variables. This practice ensures that future upgrades can be made smoothly and without complications.

/**
* @dev This empty space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[45] private __gap;

Finally, it is essential to avoid using selfdestruct() or performing delegatecall()call() to untrusted contracts. An attacker can exploit the use of these functions to destroy the logic implementation or execute custom logic. To prevent this, it is important to verify user input and not allow the contract to perform delegatecall()call() to untrusted contracts. Additionally, using delegatecall() in the logic contract is not recommended, as managing the storage layout in multiple contracts can be difficult. By following these practices, developers can minimize the risk of vulnerabilities in their contract upgradability implementations.

#6: Protect Against Front-running

Front-running has been a persistent problem where users are able to exploit the delay between the submission of a transaction and its confirmation on the blockchain to make a profit. This delay is caused by the use of a mempool, 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 the pending transactions and potentially intercept and profit from them. The mempool also provides an opportunity for miners to re-order transactions to maximize their profits, creating what is known as Miner (or Maximal) Extractable Value (MEV).

The following is an example of an auction bid function that is susceptible to front-running.

function vote(uint256 bidAmount) public {
    require(bidAmount > highestBid);
    highestBid = bidAmount;
    highestBidder = msg.sender;
}

This function allows users to place bids on an auction, but it can be vulnerable 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 then quickly submit a higher bid, which will be processed first and ultimately win the auction.

In the following version, users submit encrypted bids that are stored in a mapping. The bid amounts are obscured until the end of the bidding period.

function vote(bytes32 encryptedBid) public {
    encryptedBids[msg.sender] = encryptedBid;
}

function revealBid(uint256 bidAmount) public {
    require(auctionEnded);
    bytes32 encryptedBid = encryptedBids[msg.sender];
    require(encryptedBid != 0);
    require(keccak256(abi.encodePacked(bidAmount, secret)) == encryptedBid);
    require(bidAmount > highestBid);
    highestBid = bidAmount;
    highestBidder = msg.sender;
}

At the end of the bidding period, users can reveal their bids by submitting the original bid amount along with a secret value. The contract verifies that the hash of the bid amount and secret matches the stored encrypted bid, ensuring that the bid was submitted before the end of the bidding period. If the bid is higher than the current highest bid, it becomes the new highest bid. By obscuring the bid amount until the end of the bidding period, this function mitigates against front-running attacks.

Front-running and MEV have become a major concern in the blockchain community, and various solutions, such as encrypted transactions and Fair Sequencing Services (FSS), have been proposed to address these issues. Encrypted transactions can help prevent front-running by hiding transaction details from other users until the transaction is executed on the blockchain. FSS, on the other hand, can help reduce the impact of front-running and MEV by enabling secure off-chain ordering of transactions.

#7: Develop a Proactive Security Response Plan

Developing a clear and comprehensive response plan is crucial for dealing with security incidents. This plan should be regularly reviewed, updated, and tested for effectiveness. In the event of an incident, time is of the essence, so the plan should include clear steps for identifying, containing, and mitigating the situation.

A communication plan should be in place to keep stakeholders informed. Regular data backups are also important to prevent data loss. The plan should outline the recovery process for restoring data and systems to their previous state. Team members should be trained on the plan to ensure everyone understands their roles and responsibilities. 

A well-prepared response plan can minimize the impact of an incident and maintain trust with users and stakeholders.

#8: Conduct Routine Audits

Routine code audits are essential for maintaining the security of your application. Working with reputable auditors who specialize in smart contract security is an essential step in the development process. The auditor will examine the code for vulnerabilities and provide recommendations for improving overall security. Prioritizing and addressing identified issues and maintaining open communication with the auditor are crucial to achieving meaningful improvements in security.

Communication with the auditor is also important, as they can help to explain their findings and provide guidance on how to address any vulnerabilities. By working together, the audit results will produce meaningful improvements to the security of the application.

Conducting routine audits is a key component of any comprehensive security strategy for BNB Chain developers. Proactively identifying and addressing vulnerabilities in the code can minimize the risk of security breaches and ensure the safety of users' funds and assets.

#9: Make Use of Bounty Programs

Expanding on the previous tip, using a bounty program is a great way to incentivize the community to search for and report security vulnerabilities in your code. By offering a reward, such as tokens or other incentives, you can encourage skilled individuals to examine your code and report any potential issues they find.

It's important to have a well-defined and transparent program with clear rules and guidelines for what types of vulnerabilities are eligible for rewards and how they will be evaluated. Reputable third-party bug bounty programs can help ensure that the program is run smoothly and that rewards are fairly distributed. Having a diverse group of bounty hunters is also important, as different people will have different areas of expertise and can focus on finding issues that others may miss.

Finally, once vulnerabilities have been reported, it's crucial to act quickly and effectively to address them. The bounty program can be a useful tool for identifying vulnerabilities, but it's up to the development team to actually fix them and improve the security of their application.

#10: Educate Users About Security Best Practices

Educating Web3 users is a crucial step in building a secure ecosystem. Keeping your customers safe helps keep your platform safe. Users should be educated on best practices for protecting their accounts and sensitive information. One of the most important aspects of user education is teaching users to avoid phishing scams. Phishing scams are designed to trick users into revealing their private keys or passwords by impersonating legitimate websites or services. Users should be advised to always double-check the URL of the website they are using and to never enter their private keys or passwords on any website that they do not trust.

Strong passwords are another essential part of personal security. Users should be encouraged to use unique and complex passwords for each of their accounts, and to avoid reusing passwords across different services. Passwords should also be stored securely, using a password manager or other secure storage mechanism.

Finally, users should be reminded to protect their private keys. Private keys are the equivalent of a user's password and should be kept secret at all times. Users should avoid sharing their private keys with anyone and store them in a secure location. They should also be advised to never enter their private keys on any website or service that they do not trust.

Conclusion

Developers building smart contracts and dApps on BNB Chain must take a comprehensive approach to security to ensure the safety of their users' funds and assets. This includes having multiple layers of security practices to ensure that the code and processes are secure and free of vulnerabilities.  It is more important to be proactive in handling security vulnerabilities rather than reactive by creating a plan when exploits do occur and by properly educating all relevant users and stakeholders in security best practices.  All such measures can help significantly reduce the risk of security breaches and hacks.

Read more: https://www.certik.com/resources/blog/70dKaszIYVhmhaPcRc1MMY-top-10-security-tips-for-bnb-chain-builders

Comments

All Comments

There are no comments yet, why not be the first?

Recommended for you

  • White House: Trump will consider banning members of Congress from trading (stocks)

    White House: Trump will consider banning congressional members (stock) trading.

  • The ZKSync network has seen abnormal minting of 110 million tokens, of which about 66 million tokens have been sold continuously

    On April 15th, it was reported that there was abnormal minting of 110 million tokens on the ZKSync network, with approximately 66 million tokens already being continuously sold. According to token unlocking information, tokens belonging to the team and investors are still in a locked state. The official response on Discord stated that the team has been notified and an investigation is ongoing. There have been no further official statements at this time. Users are advised to closely follow official channels and handle related token transactions with caution.

  • BTC breaks through $86,000

    the market shows that BTC has broken through $86,000 and is now trading at $86,035.03, with a 24-hour increase of 1.63%. The market is highly volatile, so please manage your risks carefully.

  • Canada will grant a temporary six-month tariff exemption on goods imported from the U.S. for use in manufacturing, etc.

    the Canadian Department of Finance announced that Canada will grant a temporary tariff exemption of six months to goods imported from the United States for manufacturing, processing, as well as food and beverage packaging.

  • Optimum Completes $11 Million Seed Round, Led by 1kx

    Optimum has completed a $11 million seed round of financing, with 1kx leading the investment, and Robot Ventures, Finality Capital, Spartan, CMT Digital, SNZ, Triton Capital, Big Brain, CMS, Longhash, NGC, Animoca, GSR, Caladan, Reforge and others participating. Optimum is a decentralized, performance-enhancing memory layer for any blockchain, incubated by the Massachusetts Institute of Technology. (CoinDesk)

  • The world's first AI×Web3×cross-border e-commerce event "Nexus 2140" will be held in South Korea in June

    [Seoul, June 2025] The world's first AI×Web3.0×cross-border e-commerce integration event "Nexus 2140" will be held at the Goyang International Exhibition Center in South Korea from June 21 to 22, 2025. The South Korean government has joined forces with 400+ technology companies, 150+ venture capital institutions and 3000+ industry leaders around the world to explore new paradigms for digital business.

  • Executive Director of Trump’s Digital Asset Advisory Committee: The significance of Bitcoin strategic reserves lies in the recognition of BTC’s value

    Bo Hines, Executive Director of the Trump Digital Asset Advisory Committee, posted his interview on X platform, explaining the importance of the US Bitcoin strategic reserve. He stated that Trump's launch of the Bitcoin strategic reserve is to accumulate assets for the American people rather than plundering assets. At the same time, the significance of the Bitcoin strategic reserve lies in recognizing the value of BTC. Since the quantity of Bitcoin is limited, the Bitcoin strategic reserve may trigger a global competition for asset accumulation.

  • BTC breaks through $85,000

    the market shows that BTC has broken through $85,000, now trading at $85,032, with a 24-hour increase of 0.38%. The market is volatile, please manage your risks carefully.

  • Major European stock indices opened higher and ended higher, with the UK FTSE 100 index rising by 2%.

    main stock indexes in Europe opened higher and continued to rise, with the UK's FTSE 100 index up 2%, Germany's DAX index, France's CAC40 index, Italy's FTSE index, and the Euro Stoxx 50 index all up by about 2.4%. 

  • AI Big Model Empowers Cryptocurrency Market, BitradeX Leads Industry Transformation with Forward looking Layout

    The latest industry analysis from BitradeX points out that the explosion of AI big model technology is bringing revolutionary changes to the 24/7 uninterrupted operation of the cryptocurrency market. The all-weather trading characteristics and high market volatility provide unique advantages for AI enabled quantitative trading. BitradeX has been the first to launch an AI Bot product by deeply integrating cutting-edge big model technology with high concurrency quantization systems, achieving millisecond level market analysis and intelligent decision-making. The platform believes that the combination of AI and encryption will reshape the industry landscape, and in the future, the competition core of exchanges will shift from simple trading to intelligent investment services. BitradeX has taken the lead in laying out and leading this wave of change. Official website address: bitradex.com