
When I was recently asked to strengthen the security posture of a smart contract (SC) project, I faced the emerging challenge: “How to protect against software supply chain attacks in the blockchain space?” Despite extensive research, I found mostly generic security advice rather than concrete, actionable guidance tailored for SCs.
After posting on Stack Overflow (which unfortunately got closed for being “too generic”), I realized the blockchain development community needed more specific resources on this topic. This led me down a path of research and implementation that resulted in a set of security controls that I’m sharing here.
Here’s a bit of context about our project setup:
Core Framework and Libraries:
Infrastructure and Deployment:
Note: While we used Hardhat and Ethers.js, the best practices I’ll discuss are equally applicable to Foundry and Web3.js environments.
Protecting code integrity across our development-to-deployment pipeline was critical, especially given the multiple nodes, platforms, and environments involved. We couldn’t risk malicious code or bytecode compromising our smart contracts. Because smart contract-specific supply chain security guidance was nonexistent, we had to forge our path. Unsurprisingly, many of our solutions mirrored proven software development security practices. Here’s a high-level breakdown of the security controls we implemented and the rationale behind each decision.
Because NPM served as our package manager for Hardhat and OpenZeppelin dependencies, we began by implementing the NPM security guidelines recommended by OWASP and Snyk. These provide comprehensive guidance aimed at preventing supply chain attacks through package management vulnerabilities.
# Ensures exact version numbers are saved in package.json
save-exact=true
# Enables creation of package-lock.json for reproducible installs
package-lock=true
# Reduces noise during install, only shows warnings
loglevel=warn
#Enable strict integrity checks
strict-ssl=true
# Disable running scripts automatically
ignore-scripts=true
# Enable audit on install
audit=true
5. Use a local NPM proxy
You can establish a private package repository and cache using tools like Verdaccio. Alternatively, implement GitHub submodules (though this approach has become less popular). While we evaluated implementing a local NPM proxy for enhanced security, we decided the additional risk was manageable given our occasional need to manually incorporate the latest package versions.
Bytecode integrity can be verified at two critical stages:
For private blockchain deployments, you can create custom Ethers.js or Web3.js scripts to download the on-chain bytecode and compare it against your local contract_name.json file. When performing manual verification, remember to strip the metadata that gets appended to on-chain contract bytecode (see https://playground.sourcify.dev/), and note that deployed contracts don't include constructor bytecode. For proxy implementations (especially UUPS proxies), additional verification steps may be required depending on the proxy pattern used. I’ll cover these specific scenarios in detail in an upcoming post.
What security measures have worked best in your development workflow? Are there any supply chain vulnerabilities I missed, or innovative solutions you’ve implemented? Drop your thoughts below.
Securing Smart Contract Supply Chains: Best Practices for Attack Minimization was originally published in Coinmonks on Medium, where people are continuing the conversation by highlighting and responding to this story.