Full Report
Most modern software has a large amount of open-source code. Because the code is constantly used and downloaded, it opens up the potential for supply chain attacks. Despite good process and technical chops, every dependency is an unavoidable trust relationship. This article discusses how Golang tries to mitigate these risks with very explicit design decisions. In Golang, all builds are locked. The version of every dependency is set in the go.mod file. Only explicit updates to this file can change the dependencies, such as go get or go mod tidy. This is super important for security - the code in the repository for should be the source of truth and nothing else. There is no concept of latest for dependencies. This prevents a dependency from being compromised by backdooring all of the users very quickly. Everything said above is also transitive for dependencies of dependencies as well. If a dependency is compromised, it requires a specific update as a result, giving folks time to see what's going on. Version contents are guaranteed to never change - module versions are immutable. This property ensures that an existing package cannot be modified to compromise code that depends on it. So, if something is safe to run currently, we're confident it will be safe to run. A lot of cryptographic verification goes into this. Another issue that I have with NPM is related to hooks or builds running code. Golang has no post-install hooks. Additionally, the built code cannot do anything until it is actually running. In all likelihood, if you installed something, you're probably going to run it, but this does add another security boundary, though. Overall, a good look into supply chain security in Golang. I like to see that the developers put a lot of thought into the package manager of Golang.
Analysis Summary
# Best Practices: Supply Chain Security in Go (Golang)
## Overview
These practices address the risks associated with third-party software dependencies—specifically "supply chain attacks" where attackers compromise a dependency to gain access to all downstream users. Go’s design prioritizes deterministic builds, cryptographic immutability, and a "minimalist" dependency culture to reduce these risks.
## Key Recommendations
### Immediate Actions
1. **Check in Version Metadata:** Ensure both `go.mod` and `go.sum` are committed to your Version Control System (VCS). These act as the "source of truth" for your builds.
2. **Verify CI Workflows:** Confirm that CI/CD pipelines use `go build` or `go test`. Avoid running `go get` or `go mod tidy` in automated build environments to prevent silent dependency updates.
3. **Audit Dependencies:** Run `go mod graph` or `go list -m all` to visualize your current dependency tree and identify unnecessary packages.
### Short-term Improvements (1–3 months)
1. **Enforce Build Determinism:** Upgrade all environments to Go 1.16+ to ensure the `go` tool fails by default if `go.mod` is incomplete, preventing "floating" versions.
2. **Review Dependency Changes:** Treat changes to `go.mod` and `go.sum` as high-priority items during code reviews. Investigate why a version was bumped or why a new checksum was added.
3. **Adopt "Zero-Dependency" Mindset:** Evaluate existing small dependencies. If a dependency provides a trivial function, consider "copying a little code" (per Go proverbs) to eliminate the external trust relationship.
### Long-term Strategy (3+ months)
1. **Establish Dependency Vetting:** Create a formal process for approving new dependencies based on maintainer reputation, activity, and security posture.
2. **Minimize Transitive Risk:** Actively refactor code to use Go’s rich standard library (`net/http`, `crypto`, `encoding/json`) instead of third-party wrappers to keep the dependency tree shallow.
3. **Automated Vulnerability Scanning:** Integrate tools that scan the `go.sum` file against known vulnerability databases (like the Go Vulnerability Database).
## Implementation Guidance
### For Small Organizations
- **Focus on the Standard Library:** Leverage Go’s comprehensive standard library to keep your `go.mod` file as small as possible.
- **Manual Review:** Since the team is small, ensure every developer understands that a change to `go.mod` is a security-sensitive event.
### For Medium Organizations
- **Proxy Usage:** Utilize the **Go Module Mirror** (`proxy.golang.org`) to ensure availability and speed, while relying on the **Checksum Database** (`sumdb`) for integrity.
- **Internal Tooling:** Standardize build scripts to use flags like `-mod=readonly` to ensure builds never modify dependency files.
### For Large Enterprises
- **Private Proxies:** If using private repositories, configure `GOPRIVATE` correctly and consider an internal module proxy that mirrors the public `sumdb` for consistent integrity checks across the global workforce.
- **Policy Enforcement:** Implement linting rules that flag or block PRs adding dependencies with high transitive counts or restrictive licenses.
## Configuration Examples
### Enforcing Read-Only Builds
In CI/CD environments, use the following flag to ensure no dependencies are fetched or modified during the build process:
bash
go build -mod=readonly ./...
### Bypassing Proxy for Private Modules
To ensure the checksum database and proxy are used for public modules but skipped for internal ones:
bash
export GOPRIVATE=github.com/my-org/*
## Compliance Alignment
- **NIST SP 800-161 (Supply Chain Risk Management):** Go’s `go.sum` and SumDB fulfill requirements for software integrity and provenance.
- **ISO/IEC 27001:** Supports "Secure Development Policy" by providing deterministic build paths.
- **CIS Software Supply Chain Security Guide:** Directly aligns with recommendations for pinned dependencies and immutable versioning.
## Common Pitfalls to Avoid
- **Ignoring `go.sum` Changes:** Blindly accepting changes to `go.sum` in a Pull Request defeats the purpose of cryptographic verification.
- **Using "Latest":** While Go prevents "latest" from auto-updating in existing builds, developers should avoid over-using `go get -u` without reviewing what was updated.
- **Assuming "No Hooks" means "No Risk":** While Go has no post-install hooks, malicious code can still execute via `init()` functions once the application is compiled and run.
## Resources
- **Go Module Mirror & SumDB:** [https://sum.golang.org] (Defanged)
- **Go Modules Reference:** [https://go.dev/ref/mod] (Defanged)
- **Go Vulnerability Database:** [https://pkg.go.dev/vuln/] (Defanged)
- **Go Proverbs:** [https://go-proverbs.github.io] (Defanged)