What has changed in Smart Contract Security? A Five-Year Experience Report
An Article, written from the perspective of one of our managing partners, Stefan Beyer, reflecting on his experience in smart contract security.
Blockchain, DeFi, and crypto have grown at a rapid pace over the last five years. There has been an explosion of innovation where we’ve seen the development of decentralized exchanges, lending protocols, huge growth in stablecoins, and the rise of DeFi, GameFi, and NFTs.
2017 was a pivotal year for crypto. Ethereum was 2 years old, and influential protocols like Aave, Uniswap, and MakerDAO had either just launched or weren’t quite live yet. These protocols changed the game and have been forked and tweaked hundreds of times since then.
Of the current top 10 blockchains by TVL, only 2 existed in 2017 (Ethereum and Polygon which launched in late 2017), and many things that seem normal today like bridging or incentivizing liquidity, didn’t exist yet.
Despite (or perhaps because of) all of this innovation we still see a high frequency of hacks, exploits, and security breaches. As the complexity of applications has increased, so too have the security vulnerabilities.
Along with our team of auditors, I have audited hundreds of smart contracts since 2017 and have seen this space develop from simple token and auction use cases to complex financial protocols that interact with each other.
With that in mind, let’s discuss what has changed in the security landscape over the last 5 years, why it’s changed, and what can be done about the not-so-positive aspects. Our team has audited hundreds of smart contracts and has been at the forefront of blockchain security, so let’s see what can be done about the challenges developers face.
Applications have become increasingly complex, interacting with multiple internal and third-party smart contracts. The more complex a system, the more vulnerable it is, so as applications grow in complexity, so too do they grow in vulnerability.
The smart contracts we audited 5 years ago were straightforward tokens, maybe an auction or a crowd sale contract. Some might have been fairly large, but it was easy to reason about entry points and the flow through the logic. Today with DeFi, we have decentralized exchanges, lending/borrowing, staking, auto-compounding, and bridging to name a few activities. There are now large and multilayered protocols, holding significant amounts of liquidity and multiple assets all while interacting with multiple smart contracts.
It’s not only smart contracts that have grown in complexity, the nature of protocols and DAOs have become complex too. We now see governance exploits using flash loans, and economic exploits based on new and often untested tokenomics models have become common. Developers are essentially creating mini-economies that have a whole multitude of variables, attack vectors, and moving pieces.
Proxy patterns and upgradeability
Often smart contract complexity is the result of developers trying to support features that are not native to the underlying platform, such as upgradability. Developers use complex proxy patterns to work around the blockchain’s inherent immutability property.
This introduces two problems: Firstly, the proxy architecture itself with additional indirections and limitations on storage layout introduces a source for potential error, as seen in the recent Audius storage slot collision exploit.
Secondly, removing the immutability assumption causes a wider problem, in that any external contract a calling smart contract relies on might now suddenly change its behavior, thus making any protocol composition highly dangerous. Most blockchain users might not realize this but even basic contracts, such as commonly used stablecoin ERC-20 tokens, are implemented this way and could suddenly change their behavior, breaking hundreds of protocols.
While upgradability proxy patterns have a clear purpose that sometimes may justify their use, developers often misuse such tricks unnecessarily. The infamous diamond pattern is one example of unnecessary complexity.
In our audits, we see many examples of developers overengineering their architecture with good intentions. However, simplicity is the key to security.
Another way that smart contracts have become increasingly complex is due to the search for gas optimizations. The cost of operations, particularly on Ethereum, has incentivized developers to optimize their code in order to reduce costs. Whilst some of these optimizations are extremely creative engineering, they often lead to code that is hard to read and verify, and in many cases error-prone. The code is trying to do too much and shortcuts are taken which leads to vulnerabilities, attack vectors, and an increase in bugs and mistakes.
These days protocols have started interacting with and relying on each other a lot more than they did five years ago. Protocols are now seen as building blocks that can be used in combination to enable new services.
This is a great development and highlights the true purpose of DeFi. However, it also increases many risks and makes our job as security auditors much more difficult.
It is now much harder to make a statement on the security of one protocol, since minor issues in another protocol may cascade up causing catastrophic ripple effects.
One simple example of this is the common assumption that all ERC-20 tokens behave in the same way. This has led to exploits on a number of protocols that integrated incompatible token implementations. Most famously, Multichain assumed that the permit() function that many ERC-20 tokens have, would revert when an off-chain permission signature is not given. However, if a token does not implement permit() and has a non-reverting fallback function, this is executed instead. WETH, which wraps ETH and is heavily used by Muitlchain, is such a token, meaning that calls to the non-existing permit() do not revert for invalid signatures, as expected.
Logic Bugs vs Platform Footguns
One positive change we have seen in our security reviews is that most teams are now aware of the typical antipatterns found in their platform. Common vulnerabilities, such as footguns introduced by the peculiarities of the Solidity language, are now well-known.
Automated tools, such as static code analyzers, have become very good at finding these antipatterns, and are now commonly used by developers, which is great.
We now find the majority of the bugs occur in logic and cannot be covered by automated tools, instead requiring an expert review. This means that security audits can focus on the essential part of protocols, and go deeper into the logic and economics.
Reentrancy has not gone away
There is one exception to the above: Reentrancy hasn’t changed but continues to be a problem. The most well-known example was the DAO hack 6 years ago when $60 million were stolen. Despite the time that has passed, reentrancy is still an issue.
We find reentrancy still comes up in most audits that we perform. Despite there being so many high-profile cases, it is not something that developers have solved. This highlights the complexity of the issue. Making external calls to third-party smart contracts is unavoidable if we want to harness the full power of DeFi. Reentrancy is a side-effect of this, and the number of incidents in the last 5–6 years, and the changing advice over the years show that it is a hard problem.
Some platforms, such as CosmWasm, have introduced novel programming models that eliminate reentrancy at the core. This is a welcome development, and research into programming models suitable for the transactional nature of the blockchain is an important topic.
In the meantime, reentrancy may be found in unexpected places and it is surprising how many occurrences we find in the code of very capable teams, highlighting the importance of external, security-focused eyes, on code.
A lot has changed in the last five years. Protocols have advanced and become more complex, opening up new issues and attack vectors. It has been great to see the developments, advancements, and innovations that teams have brought, but we must also be wary of the security risks inherent in DeFi, blockchain, and crypto.
Our advice is to always prioritize security and work with multiple auditing firms in an ongoing manner, and think about security from the very beginning.
If you are looking for support for your project, please get in touch and we can schedule a call to discuss your needs.