There’s no perfect solution but let’s not let perfect be the enemy of good.
SSH is a good example where the host key is trust on first use. It would be nice to have this option for supply chain dependencies too (npm, ruby, etc). So your local client would reject any updates to a package if it wasn’t signed by the original key.
Like SSH, if the key had to be rotated due to compromise, you’d have to reset that trust. But at least that would be a manual process and changes would be picked up as an error by build servers.
Agreed, I see people dismiss valuable but imperfect security too often.
Google uses a similar approach for Android packages. In order to publish to the Play Store you need to upload signed with the same key. If you can't, then you submit a support request and they run some extra checks before they allow a new key.
On challenge I don't see others mention I that an attacker could publish a small, non-malicious release first. Then any manual diff review wouldn't see an issue. The next release using the attackers key would be trusted, so manual review would be skipped, but that one would contain malware.
This requires two releases by the attacker, over a period of time. Slowing down the attack is a benefit on its own. It gives package owners time to wonder, wait who pushed that last release?
SSH is such a different use case that the analogies break down. With respect, thinking about this for a few minutes should convince you that while TOFU works fine for immutable artifact signatures, it is unworkable for a system where you ever upgrade packages.
Consider that most nontrivial projects on rubygems have multiple maintainers that can publish a version. Normal collaboration models would imply that they each have a personal signing key; sharing a single signing key per project isn’t realistic (as you mentioned, rotation is another reason for this). And TOFU doesn’t work when there are multiple possible keys, such a system requires an external trust chain.
Assume for the sake of argument the above is solved. What exactly do you do when tooling alerts you that an upgraded dependency has changed keys since the last version? Either you blindly accept the new key or you investigate. If the latter hopefully you have a way to directly contact the author to verify that the rotation was legitimate. Since you probably don’t you should just compare the diff of the published artifacts. But you should have been doing this anyway, so what has the signature bought you here except false security?
Imagine a system like npm, where hundreds of transitive dependencies are upgraded all the time. Would you really want a key change in a 3rd-level dep you didn’t even know you were using to block you from installing a security fix to a primary dependency until you could somehow vet the new key? Such a system would change very quickly to Trust On First Use But Also Trust On Any Change…
Re multiple maintainers with release capabilities, each release could include a list of all authorised maintainers key, but only signed by one of them. Changing the list (or just additions as well as significant removals) would trigger a trust change in the client. Any maintainer can add/remove other maintainers (or maybe use a quorum if enough maintainers to make that workable).
Generally I wouldn’t be too happy using a dependency where there are lots of users with access to create official releases.
Re npm and transitive dependencies, this isn’t a code signing issue as much as it is a version pinning issue. Specifying latest as the version is a poor approach imo. Also the primary dependency should not be releasing a critical security fix as a breaking change. If it has to be a breaking change then there’s marginal additional effort to having to deal with the transitive dependency too given you’re already having to change things to support the primary dependency.
I’m not saying TOFU signatures would be a silver bullet. They, in conjunction with other security measures, best practices, etc, would help a lot though.
So adding or removing a maintainer is an event that causes everyone’s package installs to break? In a large enough project that would mean nearly every dependency change would trigger this condition.
> Generally I wouldn’t be too happy using a dependency where there are lots of users with access to create official releases.
I’m curious, do you use any open source software? Most larger projects have a number of maintainers with release rights.
I want to clarify that I’m not against package signing. For example, I think this proposal is well-thought-out and would be a positive change. The key reason this is better IMO is that it introduces a centralized trust mechanism and a certificate transparency log: https://github.com/rubygems/rfcs/pull/37
SSH is a good example where the host key is trust on first use. It would be nice to have this option for supply chain dependencies too (npm, ruby, etc). So your local client would reject any updates to a package if it wasn’t signed by the original key.
Like SSH, if the key had to be rotated due to compromise, you’d have to reset that trust. But at least that would be a manual process and changes would be picked up as an error by build servers.