Software Supply Chain Security: Post-SolarWinds Practices

January 11, 2021

The SolarWinds attack revealed how vulnerable software supply chains are. A compromised build system distributed malware to thousands of organizations through trusted update channels. This changes how we must think about software security.

Here’s how to secure your software supply chain.

The Supply Chain Attack Surface

Where Attacks Happen

┌─────────────────────────────────────────────────────────────────┐
│                    Software Supply Chain                         │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  Source Code ──► Dependencies ──► Build ──► Package ──► Deploy  │
│       │              │             │          │           │     │
│       ▼              ▼             ▼          ▼           ▼     │
│   Compromised   Malicious     Injected    Tampered    Replaced  │
│   developer     package       malware     artifact    binary    │
│   account                                                        │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

Attack Vectors

Source code:

Dependencies:

Build systems:

Distribution:

Software Bill of Materials (SBOM)

What Is an SBOM

An inventory of all software components:

{
  "bomFormat": "CycloneDX",
  "specVersion": "1.4",
  "version": 1,
  "metadata": {
    "component": {
      "type": "application",
      "name": "my-application",
      "version": "1.0.0"
    }
  },
  "components": [
    {
      "type": "library",
      "name": "lodash",
      "version": "4.17.21",
      "purl": "pkg:npm/lodash@4.17.21",
      "hashes": [
        {
          "alg": "SHA-256",
          "content": "cc6c1e..."
        }
      ],
      "licenses": [
        {
          "license": { "id": "MIT" }
        }
      ]
    }
  ]
}

Generating SBOMs

# For Node.js
npx @cyclonedx/cdxgen -o sbom.json

# For Go
cyclonedx-gomod mod -json > sbom.json

# For containers
syft myimage:tag -o cyclonedx-json > sbom.json

# For general use
trivy image --format cyclonedx myimage:tag > sbom.json

Using SBOMs

uses:
  vulnerability_tracking:
    - Know what's affected when CVE announced
    - Automated alerts for new vulnerabilities
    - Prioritize remediation

  license_compliance:
    - Track license obligations
    - Detect problematic licenses
    - Audit trail for legal

  incident_response:
    - "Are we affected by log4j?"
    - Instant answer from SBOM database
    - Identify all affected systems

  procurement:
    - Evaluate third-party software
    - Risk assessment
    - Vendor management

Build System Security

Secure Build Principles

principles:
  hermetic_builds:
    - No network access during build
    - All dependencies pre-fetched
    - Reproducible outputs

  minimal_access:
    - Build system can't access production
    - Credentials are scoped and short-lived
    - Audit all access

  integrity_verification:
    - Verify source before building
    - Verify dependencies before including
    - Sign outputs

  transparency:
    - Log all build activities
    - Monitor for anomalies
    - Alert on deviations

Build Isolation

# Isolated build environment
build_environment:
  container:
    image: build-env:v1  # Verified, controlled image
    network: none        # No network access
    read_only: true      # Immutable filesystem
    capabilities: []     # Drop all capabilities

  inputs:
    - source: verified git checkout
    - dependencies: pre-fetched, verified

  outputs:
    - artifacts: signed before distribution
    - sbom: generated and stored
    - attestation: build provenance recorded

SLSA Framework

Supply-chain Levels for Software Artifacts:

Level 1: Documentation of build process
Level 2: Tamper resistance of build service
Level 3: Tamper resistance of source/build
Level 4: Two-person review, hermetic builds
# SLSA Level 3+ practices
practices:
  source:
    - All changes reviewed
    - Signed commits
    - Protected branches

  build:
    - Ephemeral build environments
    - Hermetic builds
    - Reproducible builds
    - Build provenance generated

  provenance:
    - What source was built
    - What builder was used
    - What dependencies included
    - Signed attestation

Dependency Security

Dependency Verification

# Lock files capture exact versions and hashes
# package-lock.json, go.sum, requirements.txt with hashes

# Verify integrity
npm ci --ignore-scripts    # Install from lock file, no arbitrary scripts
go mod verify              # Verify checksums

# Audit for vulnerabilities
npm audit
go list -m -json all | go-mod-outdated -update

Private Registry Mirroring

# Mirror public packages internally
registry_strategy:
  external:
    - npmjs.com
    - pypi.org
    - proxy.golang.org

  internal_mirror:
    - Nexus/Artifactory
    - Cache verified packages
    - Scan before caching
    - Audit access

  policy:
    - Only use mirrored packages
    - Block direct external access
    - Review before adding new packages

Dependency Pinning

# Pin to exact versions with hashes
# requirements.txt
requests==2.26.0 \
  --hash=sha256:6c1246...

# package-lock.json
{
  "lodash": {
    "version": "4.17.21",
    "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
    "integrity": "sha512-..."
  }
}

Code Signing

Sign Build Outputs

# Sign container images with Sigstore
cosign sign --key cosign.key myregistry.io/myapp:v1.0.0

# Verify before deploying
cosign verify --key cosign.pub myregistry.io/myapp:v1.0.0

Keyless Signing

# Using Sigstore Keyless (OIDC identity)
cosign sign myregistry.io/myapp:v1.0.0
# Uses OIDC identity (GitHub, Google, etc.)
# No key management required

# Verify
cosign verify \
  --certificate-identity-regexp='.*@mycompany.com' \
  --certificate-oidc-issuer=https://accounts.google.com \
  myregistry.io/myapp:v1.0.0

Admission Control

# Kubernetes policy: only signed images
apiVersion: policy.sigstore.dev/v1alpha1
kind: ClusterImagePolicy
metadata:
  name: require-signatures
spec:
  images:
    - glob: "**"
  authorities:
    - keyless:
        url: https://fulcio.sigstore.dev
        identities:
          - issuer: https://accounts.google.com
            subject: build-service@mycompany.iam.gserviceaccount.com

Monitoring and Detection

Build Monitoring

alerts:
  - name: UnusualBuildActivity
    condition: build outside normal hours
    action: review and alert

  - name: NewDependencyAdded
    condition: dependency not in approved list
    action: security review required

  - name: BuildOutputChanged
    condition: same inputs, different outputs
    action: investigate immediately

  - name: LongBuildDuration
    condition: build takes 2x normal time
    action: investigate (possible injection)

Artifact Verification

# In deployment pipeline
steps:
  - name: Verify signature
    run: cosign verify --key cosign.pub $IMAGE

  - name: Check SBOM vulnerabilities
    run: grype sbom:$SBOM_PATH --fail-on high

  - name: Verify attestations
    run: cosign verify-attestation --key cosign.pub $IMAGE

Implementation Roadmap

Phase 1: Visibility

- [ ] Generate SBOMs for all applications
- [ ] Inventory all dependencies
- [ ] Map build processes
- [ ] Identify critical paths

Phase 2: Hardening

- [ ] Implement dependency pinning
- [ ] Enable vulnerability scanning
- [ ] Isolate build environments
- [ ] Add code signing

Phase 3: Verification

- [ ] Require signatures for deployment
- [ ] Implement admission control
- [ ] Add provenance verification
- [ ] Continuous monitoring

Key Takeaways

Supply chain security is no longer optional. The threat is real, the attacks are sophisticated, and the consequences are severe.