Software Supply Chain Security

Software Update Signing and Verification: Getting It Right

Signed updates are table stakes for software distribution. But the signing and verification process has pitfalls that undermine the entire security model.

James
Senior Security Analyst
5 min read

Every software vendor signs their updates. Or at least they should. Code signing is the mechanism that lets users and systems verify that an update came from the legitimate publisher and has not been modified in transit.

The concept is simple: hash the update, sign the hash with your private key, distribute the signature alongside the update, and have the client verify the signature before applying the update. In practice, dozens of implementation details can undermine this entire model.

The SolarWinds attack succeeded despite Orion updates being signed. The 3CX attack distributed signed, trojanized binaries. Stuxnet used stolen code-signing certificates. Code signing is necessary but not sufficient -- the details of how you sign and verify matter enormously.

The Signing Process

Key Management

The private signing key is the crown jewel. If it is stolen, the attacker can sign malicious updates that pass verification. If it is lost, you cannot sign legitimate updates.

Hardware Security Modules (HSMs). Store signing keys in HSMs that prevent key extraction. The signing operation happens inside the HSM, and the key never exists in software. Cloud HSMs (AWS CloudHSM, Azure Dedicated HSM, Google Cloud HSM) make this accessible without physical hardware management.

Key rotation. Plan for regular key rotation and emergency rotation if compromise is suspected. Your update client needs to accept signatures from both old and new keys during the transition period.

Access control. Limit who can trigger signing operations. Require multiple approvals for signing production releases. Log all signing operations for audit.

What to Sign

Sign the complete update package, not just individual files. If you sign files individually, an attacker who obtains an old signed file can substitute it into a new update package (downgrade attack).

Include a manifest that lists all files, their hashes, and the target version in the signed payload. The client should verify the manifest and then verify each file against the manifest hashes.

Timestamp Signing

Code-signing certificates expire. Without a trusted timestamp, signatures made with an expired certificate are rejected, even if the certificate was valid at signing time.

Use a trusted timestamp authority (TSA) to timestamp your signatures. This proves the signature was created while the certificate was valid, allowing clients to accept the signature even after certificate expiration.

The Verification Process

Verify Before Applying

This sounds obvious, but multiple real-world attacks have exploited update systems that apply the update before verifying the signature, then roll back if verification fails. The window between application and rollback is an exploitation opportunity.

Always verify the complete signature before extracting, installing, or executing any part of the update.

Certificate Chain Validation

Verify the entire certificate chain from the signing certificate to a trusted root CA. Check certificate revocation status (through CRL or OCSP). Reject certificates that are expired, revoked, or issued by untrusted CAs.

Pin the Signing Certificate

If your software only receives updates from your organization, pin the expected signing certificate (or at least the issuing CA). This prevents an attacker who obtains a valid code-signing certificate from a different CA from signing malicious updates for your software.

Version Checking

Include the target version in the signed payload and verify that updates are applied in the correct order. Reject updates that would downgrade the software to an earlier version.

Without version checking, an attacker who captures a legitimately signed old update can replay it to install a version with known vulnerabilities.

Common Mistakes

Signing with the build server. If the signing key is on the build server, a compromised build process can sign malicious artifacts. Move signing to a dedicated, hardened signing service that is separate from the build pipeline.

Not verifying on the client. Implementing signing without client-side verification provides zero security. It just costs money for code-signing certificates.

Using self-signed certificates. Self-signed certificates make key management your sole responsibility. If you cannot use a public CA, implement your own key distribution mechanism carefully.

Ignoring revocation. If a signing key is compromised, you need a way to revoke it so clients stop accepting signatures made with it. Plan for revocation before you need it.

The TUF Framework

The Update Framework (TUF) is a specification designed for securing software update systems. It addresses attacks that simple code signing does not: key compromise, rollback attacks, mix-and-match attacks (combining files from different valid updates), and freeze attacks (serving stale updates).

TUF uses multiple keys with different roles (root, targets, snapshot, timestamp), each with independent expiration and rotation. Compromising any single key does not fully compromise the update system.

Projects like Python PEP 458 (PyPI), Sigstore, and various Linux distribution update systems have adopted TUF principles.

How Safeguard.sh Helps

Safeguard.sh helps you verify the integrity of software updates across your supply chain. Our platform checks signature validity, monitors certificate status, and alerts you to unsigned or improperly signed artifacts. Combined with SBOM generation and vulnerability tracking, Safeguard.sh ensures that every component in your software pipeline is authenticated and verified.

Never miss an update

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