Dependency Security

Golang Module Security and Verification

Securing your Go module supply chain with checksum databases, GOPROXY, and vendor directories.

James
Senior Security Engineer
5 min read

Go's module system was designed with supply chain security in mind from the start. The checksum database, module proxy, and vendor directory give Go developers stronger default protections than most ecosystems. But defaults are not enough. Here is how to use these tools effectively.

Go's Built-In Protections

Go modules have three security mechanisms that most other ecosystems lack:

  1. The Go checksum database (sum.golang.org). Every module version's hash is recorded in a tamper-evident log. When you download a module, Go verifies its hash against this public database.
  2. The Go module proxy (proxy.golang.org). A caching proxy that serves immutable module versions. Once a version is cached, even if the source repository is modified, the proxy serves the original.
  3. go.sum file. A local record of expected hashes for every module in your dependency tree.

These three mechanisms together provide strong guarantees against tampering, but only if they are configured correctly.

Understanding go.sum

The go.sum file contains cryptographic hashes for every dependency:

golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH3Ber6b6kFNcd79Svls...
golang.org/x/crypto v0.14.0/go.mod h1:MVMfS2rHDh3ePYaJ3CC11T...

Each entry has two hashes: one for the module content and one for the go.mod file. These hashes are verified against the checksum database on first download.

Critical rule: always commit go.sum. This file is your lockfile equivalent. Without it, builds are not reproducible and hash verification cannot work.

GOPROXY Configuration

The GOPROXY environment variable controls where Go fetches modules:

# Default
GOPROXY=https://proxy.golang.org,direct

# Using a corporate proxy
GOPROXY=https://goproxy.yourcompany.com,https://proxy.golang.org,direct

The direct fallback means Go will fetch directly from the source repository if the proxy does not have the module. In a high-security environment, remove direct:

GOPROXY=https://goproxy.yourcompany.com,https://proxy.golang.org,off

The off keyword means the build fails if the module is not available through the configured proxies. This prevents fetching from untrusted sources.

GONOSUMCHECK and GONOSUMDB

These variables control which modules skip checksum verification:

# Skip checksum verification for private modules
GONOSUMCHECK=github.com/yourcompany/*
GONOSUMDB=github.com/yourcompany/*
GOPRIVATE=github.com/yourcompany/*

Set GOPRIVATE for your internal modules. This automatically sets both GONOSUMCHECK and GONOSUMDB for those modules, since private module hashes cannot be in the public checksum database.

Vendor Directory

The vendor directory is the strongest supply chain protection in Go. It stores a complete copy of all dependencies in your repository:

go mod vendor

When you build with -mod=vendor, Go uses only the vendored code:

go build -mod=vendor ./...

Advantages:

  • Complete offline builds. No network access required.
  • You can review every line of dependency code in your repository.
  • Immune to registry outages, proxy issues, or repository deletions.
  • Trivial to audit with standard code review tools.

Disadvantages:

  • Repository size increases.
  • Vendored code needs to be updated explicitly.

For production applications, vendoring is worth the tradeoff. For libraries, it is unnecessary since consumers will manage their own dependency trees.

Defending Against Dependency Confusion in Go

Go modules use full repository paths as identifiers (github.com/user/repo), which provides natural namespacing. However, dependency confusion is still possible if:

  • You use a replace directive pointing to a different repository.
  • You have GOPROXY configured to check multiple sources.
  • Your private module names could collide with public ones.

Defenses:

  1. Use GOPRIVATE to ensure private modules never hit public infrastructure.
  2. Audit replace directives in go.mod carefully.
  3. Use your corporate proxy as the first entry in GOPROXY.

Vulnerability Scanning

govulncheck

govulncheck is the official Go vulnerability scanner from the Go team:

go install golang.org/x/vuln/cmd/govulncheck@latest
govulncheck ./...

What makes govulncheck special is that it performs reachability analysis. It does not just check if a vulnerable package is in your dependency tree. It checks whether your code actually calls the vulnerable function. This dramatically reduces false positives.

# Scan a binary
govulncheck -mode=binary ./myapp

# JSON output for CI integration
govulncheck -json ./...

Run govulncheck in CI and fail builds on reachable vulnerabilities.

nancy

nancy from Sonatype checks Go modules against the OSS Index:

go list -json -deps ./... | nancy sleuth

It is a useful complement to govulncheck since it uses a different vulnerability database.

Secure Coding with Go Modules

Minimal version selection

Go uses minimal version selection (MVS) instead of the newest-version-wins strategy used by most package managers. When two dependencies require different versions of the same module, Go picks the minimum version that satisfies all requirements. This is inherently more conservative and predictable.

Retracted versions

Module authors can retract versions that should not be used:

// go.mod
retract (
    v1.0.0 // Contains security vulnerability
    [v1.1.0, v1.2.0] // Broken release range
)

go get will warn about retracted versions. Check for retractions when auditing your dependency tree.

Module graph pruning

Go 1.17+ prunes the module graph to include only modules that actually contribute packages to your build. This reduces the attack surface by excluding modules that are in the dependency tree but not actually used.

SBOM Generation

Generate a CycloneDX SBOM for your Go application:

go install github.com/CycloneDX/cyclonedx-gomod/cmd/cyclonedx-gomod@latest
cyclonedx-gomod app -json -output sbom.json

For binaries, you can also extract dependency information from the binary itself:

go version -m ./myapp

Go embeds module information in compiled binaries by default, which makes SBOM generation possible even from production artifacts.

How Safeguard.sh Helps

Safeguard.sh leverages Go's built-in security features and adds continuous monitoring on top. It ingests your Go SBOMs, tracks module versions across your services, and correlates against vulnerability databases including the Go vulnerability database. When govulncheck finds a reachable vulnerability, Safeguard.sh maps that finding to every affected service in your organization and helps you prioritize remediation. It provides the organizational-level visibility that individual tool runs cannot.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.