Developer's Guide To Web3 Security

Web3 has finally entered the mainstream consumer space, and its effect is undeniably strong. Even the big tech realizes this, as it has started building the supporting infrastructure. This moment also marks a major shift in the tech landscape as we transition from the company-owned Web2 to the user-owned Web3 model.

But as with any new technology, Web3 comes with a new set of challenges, and security remains a big issue. According to Certik — a leading Web3 security firm, DeFi has lost more than $2 billion in the first two quarters of 2022 alone, which just shows how bad the situation is.

Cyber attacks have become increasingly common, and there has been a parallel surge in demand for security professionals in the industry. Since Web3 is still in its nascency, this presents a perfect opportunity for people looking to get a headstart in this space.

How Secure is Web3?

Security in Web3 revolves around two central ideas, decentralization and strong encryption. These features are native to blockchain technology, a self-protecting database that resists mutation due to its design. But unlike traditional databases, a blockchain has several layers of protection.

Every chunk of data on a blockchain is a transaction, and to verify its authenticity, a blockchain uses a ‘Consensus Mechanism.’ This algorithm requires all the miner nodes on a blockchain to reach a common ground for a transaction before it can get recorded. Additionally, every transaction is asymmetrically encrypted, and its ownership can be justified.

Blockchain security is established on one simple fact; the computing power required to create fake transactions has to be greater than the combined processing power of all participating miner nodes. As the blockchain gets bigger and computers naturally get faster, the cost of brute-force or data corruption attacks on a blockchain becomes impossibly high.

But even with this security, Web3 systems are still constantly at the receiving end of cyber-attacks. So much so that it has painted an overall grim picture for some people in the community. And this raises the question, is Web3 more secure than Web2? The short answer is Yes.

The reasoning behind this is quite simple, most of the security breaches we see in the blockchain world today (except some) are not related to the functioning of the blockchains themselves. These attacks mostly occur on the application layer or from newer technologies built around the blockchain that facilitate cross-chain communication.

Web3 Security Vectors

The list of attack vectors in Web3 is long and constantly growing. For the sake of this article, we’ll limit the scope to only those pathways which depend upon the functioning of Decentralized Applications.

Smart Contract Vulnerabilities

Understanding Smart Contract Vulnerabilities

Although Web3 has been around for a while now, the ecosystem for writing smart contracts is immature. Writing blockchain apps requires a deep understanding of smart contract programming languages and an in-depth knowledge of blockchain networks. And due to the absence of a mature ecosystem, developing enterprise-grade blockchain apps is quite difficult.

The EVM is still experimental in nature, and bugs are easier to induce than detect. A good example of this would be the re-entrance attack which only takes the programmers to incorrectly structure the sequence of operations for the vulnerability to take effect. SWC registry records some of the most common vulnerabilities, which we’ll cover in a little detail later.

Programming vulnerabilities are the leading security concern even today. Smart Contracts ideally need to go through several stages of security audits before they are considered ready for production.

DeFi Semantics

Semantic-Based Attacks Are Hard to Detect

Web3 provides a native ecosystem for finance as cryptocurrencies are embedded right into its framework. However, this has also created problems for developers and businesses as attackers now have easier and more direct access to it. Additionally, the functioning of a DApp is directly dependent upon the liquidity of the environment, which, as of today, is quite poor.

This allows more sophisticated, semantic-based attacks to occur. One of the simplest forms of this attack is market manipulation in DEXs to create artificial pumps and dumps. Attackers can use features of DeFi like lending, borrowing, and flash loans and combine them to attack the entire system.

The price oracle manipulation attack also comes under this category, where attackers use the weakness of on-chain oracles to manipulate prices through flash loans or borrows. This is now used to artificially increase the prices, leading to liquidations and a steep fall in the token price.

Compared to smart contract weakness-based attacks, these are harder to detect. And to fully understand and assess risk, businesses need to hire experts who understand the high-level semantics of a DeFi application.

Blockchain Trilemma

The Blockchain Trilemma: Decentralized vs Scalable vs Secure.

Unlike the other two weaknesses, Blockchain Trilemma is related to the weakness of blockchain and its surrounding technologies. And it refers to the notion that blockchain systems cannot simultaneously be highly scalable, secure, and decentralized. Until recent innovations, this has more or less held true, especially in the case of Layer–1 networks.

The idea originates from the CAP theorem, formulated in the 1980s. And according to the theorem, amongst security, scalability, and decentralization, a distributed system can only provide two of the features at once. Blockchain is a distributed system that follows this theorem.

Centralization of resources improves speed, but it also puts all responsibility on a single entity, reducing security. On the other hand, decentralization improves security but slows down a network considerably. Most high-TPS Layer–1 networks still suffer from this problem. The trilemma is further proven right in the case of CEXs and DEXs, where each has its benefits over the other.

However, recent innovations in the blockchain space have created a pathway for Layer–1 networks to scale while keeping their decentralization and security intact. Layer–2 scaling solutions like state channels, sidechains, and nested chains have greatly improved the scalability of Layer–1 networks without harming their security.

Ensuring Security in An Unstable Environment

Web3 gets attacked often because it is still in its elementary phase, and these attacks are more like growing pains. Opposed to Web2 applications, which follow the conventional client-server architecture, the rules and frameworks concerning the security of Web3 apps are still under development.

Moreover, since there’s no formal Web3 consortium or a central standards organization, a lot of development around the security of Web3 applications is scattered and distributed amongst several independent organizations. As such, securing Web3 applications is currently more challenging than Web2.

Even some of the most popular products like Binance, Uniswap, tornado, and Harmony face regular attacks. While ensuring complete security in this environment is unrealistic, we can get close to it by following simple rules; Given below are the steps you can take as a developer to secure your Decentralized Apps.

Following The Design Standards

Web3 companies have different solutions for a single problem, many of which are new and largely untested in real-world environments. This is bad from a security standpoint and puts the business and its customers at risk.

However, the risk can be reduced significantly by adhering to the coding standards and following some rules. Colloquially speaking, a design pattern is a general reusable solution to a given problem; whose security is well established.

When it comes to writing smart contracts, design patterns deal with different aspects of a DeFi application, such as optimizing gas costs, safely handling transactions, and securely managing storage. Smart Contract developers can use the SWC registry as their go-to handbook for writing coherent and secure code. We’ve discussed a few of those below.

Withdrawal Pattern

Handling transactions is one of the most trivial but sensitive tasks, and several ways exist. Solidity provides send, transfer and call functions for transferring ether through a smart contract.

Although all three of these functions are secure and can be used for making transfers, there are certain quirks about making transactions in practice. We’ll use the smart contract below (taken from Solidity Docs) to serve our example.

pragma solidity ^0.8.4;
contract SendContract {

address payable public richest;
uint public mostSent;
error NotEnoughEther();

constructor() payable {
richest = payable(msg.sender);
mostSent = msg.value;
}

function becomeRichest() public payable {
if (msg.value <= mostSent) revert NotEnoughEther();
richest.transfer(msg.value);
richest = payable(msg.sender);
mostSent = msg.value;
}}

Source: Solidity Docs — Withdrawal Pattern (Become The Richest Contract)

The following contract makes a direct transfer to the receiver, which puts the entire transaction burden on the sender. If the transaction fails, it will stop the functioning of our contract, and if any of the other users have their funds in this DApp, they will forever be locked.

Luckily for us, the fix is quite simple. Our goal here is not to bear the responsibility of the transaction.

function becomeRichest() public payable {
if (msg.value <= mostSent) revert NotEnoughEther();
pendingWithdrawals[richest] += msg.value;
richest = msg.sender;
mostSent = msg.value;
}

function withdraw() public {
uint amount = pendingWithdrawals[msg.sender];
pendingWithdrawals[msg.sender] = 0;
payable(msg.sender).transfer(amount);
}

As seen above, the simplest fix is creating a function that allows the fund receiver to withdraw their amount from a mapping that holds the information of their withdrawal. The withdrawal pattern provides a safe way to transfer ether so that it does not block the functioning of a smart contract in case of failure.

Avoiding Re-entrancy

Re-entrancy is a vulnerability that arises from how we structure code in a smart contract. It stops the execution of a payable function by trapping it inside a recursive call. It can be better understood using the example below.

mapping (address => uint) private userBalances;
function withdrawBalance() public {
uint amountToWithdraw = userBalances[msg.sender];
(bool success, ) = msg.sender.call.value(amountToWithdraw)(“”); 
require(success);
userBalances[msg.sender] = 0;
}

Source: Consensys.org — Reentrancy

The withdrawBalance function allows the users to withdraw their funds. However, it updates the user fund allocation after making the transfer. This means a user can drain this contract of all its funds by trapping the ETH transfer inside a recursive call. And this is done through a fallback function that calls the withdrawBalance method recursively.

Re-entrancy Explained

This vulnerability can drain a contract of all its funds and is a classic example of how smart contract programming differs from Web2 development and design. A simple fix would be to update the state variable before the transfer occurs, as in the example we’ve provided below.

function withdrawBalance() public {
uint amountToWithdraw = userBalances[msg.sender];
userBalances[msg.sender] = 0;
require(amountToWithdraw > 0);
msg.sender.call.value(amountToWithdraw)(“”); 
}

Locking Unlocked or Floating Pragma

Pragma is a compiler directive that tells EVM about the versions of Solidity our smart contract can execute. Unlocking the pragma means the smart contract is open to execute on untested and experimental versions of solidity. And this can introduce fatal bugs to our application.

pragma solidity ^0.8.0;
// Should be written as
pragma solidity 0.8.25;

The first line suggests that all the versions of the solidity compiler, starting from version 8, can be used for executing this contract. This has been fixed in the third line, where the pragma is locked. When writing this article, Solidity version eight remains the standard.

Unencrypted Data on-chain

A common misconception amongst beginner blockchain developers is that the contents of a privately marked variable are hidden. However, that’s not the case. Private in the context of blockchains and smart contracts means that the variable is inaccessible to other contracts.

The data of all contract variables is stored on the blockchain and can be viewed by anyone. Although data storage contracts exist, they’re not generally built for storing sensitive information. And the developers must ensure that any sensitive data stored on a blockchain is encrypted.

Preparing For Failure

Blockchain technology is experimental, and it is bound to fail. Even flawlessly written code is susceptible to vulnerabilities and underlying architecture problems — which surface from time to time. In the world of Web2, such situations would usually be handled through patches or hotfixes; but since a blockchain works differently, security needs to be implemented by design.

This means that we need to make our smart contracts tolerant of failures. And this is important because a blockchain is an immutable database, and a smart contract that has once been deployed, cannot be deleted. A smart contract must follow three failure control aspects to gracefully deal with such issues.

  • Pausing the contract — This works like a circuit breaker for when security has been breached. It freezes the functioning of the application and ensures no further exploitation occurs.
  • Rate limiting — As the name suggests, it means limiting the maximum usage of the different metrics in our app. This includes fund withdrawals, cooldown time, and successive withdrawals.
  • Upgrade path — Designing smart contracts to be upgradable is an absolute must. This also means having an effective upgrade plan and a procedure for introducing improvements or bug fixes.

Failure control protocols are built into all apps before they are rolled out for production. It is a lifesaver in situations with a catastrophic failure or vulnerability in the system. The recent Binance bridge attack and the handling of the situation go to show how important a failure control procedure can be.

On October 6th, 2022, the Binance bridge was drained for as much as $500 million. These funds were then moved across wallets on the smart chain network to disperse and make it hard to track. Binance prevented any further exploitation of their bridge by pausing their smart chain network and introducing new changes with the fork, which solved the previous vulnerabilities.

Designing Apps on Layer–3 Blockchains

Layer–1 represents the first layer of the blockchain and manages all the network's essential tasks. Because of the many operations that this tier has to manage, it faces issues regarding scalability. These problems are mitigated through Layer–2 scaling solutions such as state channels, sidechains, and plasma networks.

Layer–2 networks work by forming an arrangement that decouples acknowledgment and settlement of data. This boosts the network speed and also introduces interoperability. But despite offering higher speeds Layer–2 networks have failed to gain mainstream popularity, as they do not inherit the liquidity of their parent chains. Good liquidity triumphs over gains in speed, and apps continue to be developed on Layer–1 networks.

Over the years, this has created many problems for businesses. The absence of a mature ecosystem has made the development of blockchain apps complex and expensive. On one end, developers can reap the benefits of high liquidity, and on the other, they can use high scalability and interoperability to offer smoother user experiences.

These problems are now solved by Layer–3 networks, which perform the task of connecting, and pooling liquidity of Layer–1 & 2 networks. And in doing so, Layer–3 networks eliminate the redundancy that plagues blockchain development. Layer–3 acts as an application layer, simplifying the development of decentralized apps.

Layer–3 protocols like the Yellow Network bring much-needed reliability and security to Web3. By providing deep liquidity, high interoperability, and scalability, Layer–3 creates an ideal working environment for DApps. Deep liquidity offers protection from price manipulations, while the SDK provides a simpler interface for making apps interoperable.

The Web3 Security Stack

Security is a broad field that requires knowledge of a diverse set of skills. Although there are many languages and tools to cover, you can get started using just a few. And depending on your goals and area of interest, your tech stack can have different variations of the one we have mentioned here.

Web3 Programming Languages

Web3 & Blockchain programming languages can be divided into two categories based on their use. Application programming languages are used for writing smart contracts and developing decentralized apps. Whereas Low-Level languages are used for the development of blockchain networks.

Go — Imagine C, but with a modern syntax, memory safety, structured typing, and garbage collection; that’s exactly what Go is. Go was developed at Google as a “modern C.” It is used for building fast and reliable modern blockchain systems.

Go’s low-level capabilities allow the developers to have fine-grain hardware control, while its modern syntax and features boost productivity and make long-term maintenance of blockchain products easy.

Rust — Rust was developed by the Mozilla corporation to be the modern C++. It is a popular contender to Go for blockchain development as it offers similar features and is used in major chains such as Solana and PolkaDot.

But unlike Go, Rust has controllable memory management. Developers can harness this feature to make extremely fast and efficient blockchain systems. Rust is also popularly used as a smart contract programming language due to its easy-to-learn syntax.

Python — Unless you live under a rock, Python should be no stranger. It is a general-purpose programming language with a simple syntax, which has been designed keeping code readability in mind.

It has many libraries and can be used to write smart contracts for blockchains like AlgoRand. It is also heavily used in smart contract vulnerability tools such as Mythril and Slither, which we’ve discussed in the coming sections.

JavaScript — JavaScript is the native language of the internet, and it is here to keep that title. JavaScript offers first-class support for blockchain development and has been a direct inspiration for languages like solidity.

It has a small learning curve and the best support for blockchain app development. With npm and the NodeJs ecosystem, JavaScript is a powerhouse of tools you can use to easily bootstrap a blockchain app in seconds.

Solidity— When it comes to writing smart contracts, Solidity is arguably the most popular programming language. It is native to the Ethereum ecosystem and is supported by other major blockchains such as Avalanche and Binance.

It bears many similarities with curly-bracket languages like Java and JavaScript and is fairly easy to grasp. Another thing to note is that amongst all smart contract programming languages, Solidity has the best support and the most mature ecosystem of libraries and frameworks.

Vyper — Vyper is a relatively new pythonic language that recently gained traction. It has an easy syntax, and its compilation targets the EVM. Vyper was developed to make blockchain programming less complex and eliminate the bugs arising because of Solidity's code structures.

Motoko— Although less popular, Motoko is used for application development for the ICP blockchain. The Internet Computer is a blockchain that aims to reform the centralized internet. It is a decentralized cloud computing platform where Motoko is used to “model” protocols and apps.

Yul — Yul is an Intermediate EVM-based language that was designed to make optimizations on Solidity code. Its primary purpose is to improve the gas experience and produce a more efficient target bytecode that executes on the EVM.

Web3 Security Analysis Tools

Security analysis tools are the swiss army knife in the world of infosec and are of great help to security engineers and developers alike. Unlike programming languages, these tools have a smaller learning curve and are fairly easy to use.

Slither— Developed by Crytic, it is the most popular free tool used for static analysis of solidity code. It’s written completely in python and features a vast array of vulnerability detectors. Slither also has its own API, which the developers can use to write custom vulnerability analyzers.

Mythril is part of the Mythx tool suite provided by Consensys for enterprises. But unlike Mythx, it is completely free of use. It supports symbolic analysis and SMT solving for smart contracts. Mythril works on bytecode and supports multiple EVM-compatible blockchains.

Echidna— Echidna is another famous tool developed by Crytic. It is written in Haskell and is used for Fuzz-testing Solidity contracts. It runs a series of tests against smart contracts to test their security in real-world scenarios. Echidna is modular in nature and can be extended with custom tests.

Manticore — Manticore is strictly a symbolic execution tool used to explore all possible states a smart contract can reach within its capacity range. It also records inputs that lead a contract to a given state in case of errors.

All these tools are a part of the Ethereum security toolbox, which presents them in a singular docker image. This makes it all the more convenient to set up and work with them collectively rather than installing them as separate packages.

Blockchain Development Frameworks

Outside the realm of Web3, blockchain systems have a ton of uses and have been in popular use by the industry for over a decade. Since developing a blockchain system from scratch takes time and is quite expensive, the industry has collaborated and come up with solutions for developing custom blockchains using pre-existing tools and frameworks.

Hyperledger Fabric — Hosted by the Linux Foundation, Hyperledger Fabric is a permissioned blockchain platform built for the enterprise. It has a modular plug-and-play architecture that companies such as Walmart and Sony use. It offers a ton of pre-built consensus mechanisms and configurations, along with the ability to add custom features on the go.

Ethereum— Ethereum is the world’s first and arguably most popular blockchain development framework. It offers the most advanced set of features and a Turing complete bytecode interpreter called the EVM. Ethereum forms the basis of modern blockchain networks today and has a thriving community.

EOSIO — EOSIA is a blockchain development framework developed to meet high demands. It is a highly configurable, developer-friendly platform for developing high-throughput permissioned blockchain networks. EOSIA uses C++ for smart contracts and has great community support.

Web3 Security Roles

Web3 security is all about connecting the dots, protecting systems, and creating dependable recovery plans. Security roles are more serious than development, and given the current scarcity of talent in Web3, the demand is high. Many of these roles have overlapping responsibilities, making transitioning from one role to another quite easy.

  • Smart Contract Auditor — As the name suggests, your job is to analyze smart contracts, detect vulnerabilities, and bulletproof the code before it goes for production. Some companies might also require you to mentor fellow engineers over the best security practices.
  • Blockchain Security Engineer — As a security engineer, your job is to secure the entire end-to-end blockchain. This includes auditing smart contracts, securing blockchain infrastructure, creating custom security tools, and conducting in-depth research on the latest attacks.
  • Security Lead — Similar to the security engineer role, your job is to secure the entire blockchain infrastructure. But this goes well beyond that, as you are also required to create custom infosec plans and make important decisions about the security of the business and its clients.

Resources To Learn And Practice Web3 Security

Creating a portfolio for security roles is slightly tricky as this area requires a lot of experience and is usually not beginner-friendly. Luckily, there are plenty of resources online where we can practice and testify our skills for free. Moreover, many platforms offer bug-bounty programs to incentivize learning and boost participation.

  • Damn Vulnerable DeFi — It is a hacking war game where users/participants go through a series of smart contracts (the equivalent of levels) to either stop their functioning, drain them of funds or cause other anomalies.
  • Ethernaut — Similar to Damn Vulnerable DeFi, Ethernaut is a hacking wargame inspired by overthewire. It also has a historical catalog of some of the biggest attacks, which appear as levels.
  • Immunefy — Backed by Chainlink and Sushiswap, Immunefy is a Web3 bug bounty program founded in 2020. It offers some of the biggest bounties from major Web3 orgs that are updated frequently.
  • HackenProof — Hackenproof is Hacken’s bug-hunting program. Hacken is a leading name in Web3 security, and just like Immunefy, Hackenproof offers huge bounties and is associated with big orgs like FTX and CoinGecko.

If you prefer a structured way of learning, plenty of DAOs and Web3 cohorts offer tutored sessions. They also have an active community that will help you at every step.

  • Buildspace — Buildspace is perhaps one of the most famous DAOs. Whether you’re experienced, just starting out, or have cool ideas to work on, Buildspace is the perfect place to start with Web3 as quickly as possible.
  • Learn Web3 — With an active community of over 50k members (and growing), Learn Web3 is the fastest-growing DAO. It offers curated learning paths and sponsored tracks to help accelerate your learning.

Closing Thoughts

Security is paramount to every business, and to become truly disruptive and viable, Web3 needs to become more mature. Ultimately, the success of Web3 is tied to how secure it is. And just like the internet, our requirements must evolve and adapt to this new decentralized and transparent web.

Since Web3 is extremely fast-paced, and to keep ourselves secure, we need to stay ahead of the curve. As a security professional, you need an abstract understanding and nuts-and-bolts knowledge of the technology at hand. And while there’s no secret sauce to achieving complete security, an appetite for learning helps.