Put it like this: if an attacker compromised Rust's website and put in their own malicious Rust installer, macOS and Windows would both show a very scary warning that's like "This is unsigned! Don't run this!" (macOS would even refuse to run it unless you did the right-click to open trick). A Linux package manager would refuse to install such a package outright, unless you --forced it. Not so with curl-to-bash, it would just silently compromise your computer.
Basically, if you believe that code signing is a good thing for security (and I think we all do), curl-to-bash is awful security practice, and you should manually review the script.
Perhaps modify the installation script, something like `curl | some-verification-tool | bash`
(PowerShell script signing is based on standard Windows codesigning certificates, but of course this hypothetical bash script signature verifier can use GPG keys instead.)
- Store the install script on www.xyz.com/install.sh
- Store the hash of the install script on www.abc.com/hash.dat
Then in the installation command:
- curl the install script from www.xyz.com/install.sh
- hash it
- curl the hash from www.abc.com/hash.dat
- compare the computed hash with the "curl'd hash"
- only if they match, pipe www.xyz.com/install.sh to bash
In that case, a malicious actor would need to hack at least two locations to be successful.
But signing is largely a moot point as people will follow the instructions on the website where a clever attacker would simply create their own cryptographic keys sign it and provide instructions for adding it to your keychain to the compromised website. Public key crypto does not solve establishing trust for an actor. It can only do so through delegation which as we have seen with the deprecation of EV certificates (CAs no longer being trusted to establish the identity of legal persons) is not something that is easy to get right.
Certainly it is better to trust the maintainers of your operating system/package manager than a random website. However they only sign things where they have done due-diligence on the project, which is a slow process, too slow for projects with faster release cycle such as Docker or Rust.
Public crypto can help carry the established trust. Say you track some developer's work and the dev behaves consistently in a trustworthy manner, his signing key can be used to help carry this trust forward, so that after a while you don't need to keep checking and just rely on the assumption that dev is probably honest going forward + crypto to verify you're really using this developer's output.
No need for delegation. Delegation doesn't solve trust anyway, only identity verification.
There is no reason in principle why a shell could not implement digital signatures. bash could have a "--require-signature" option where it looks for a GPG signature appended to the end of the script, and refuses to run the script if the signature is missing or the signer is untrusted.
(No idea if the Bash maintainers would be willing to accept a patch adding such a feature if someone were to write one; but, signed scripts is something supported by PowerShell, although most people's experience with it is limited to turning off the default setting which requires all scripts to be signed.)
A related question – what's the difference between downloading an unsigned script over HTTPS versus downloading a signed script (whether over HTTPS or just plain HTTP)? Ideally, the code signing is done offline, so an attacker that compromises the HTTPS server gets the HTTPS private key but not the code signing private key. However, I suspect that ideal often isn't actually obtained; offline code signing can be a pain for CI. If someone has a CI pipeline which signs the code and then deploys it to the HTTPS server, then all an attacker has to do is compromise access to that CI pipeline and neither HTTPS nor code signing will stop them.
They currently don't, though, which is the point :)
The reason in principle this might not work is that different distros and os's trust different keys. Fedora trusts different keys than Ubuntu, than Arch, than macOS, etc.
> Ideally, the code signing is done offline, so an attacker that compromises the HTTPS server gets the HTTPS private key but not the code signing private key.
I don't think this is necessarily the case. Even if you gain access to the server, HTTPS keys are usually stored in such a way that only root can read them, right? So if you only compromised a non-root account, you wouldn't necessarily be able to read them (I might be wrong about this, I don't do this kind of thing professionally).
Also, you could imagine that the script is not stored as a file, but in a database or something, and then you could compromise it with SQL injection or a similar technique. That would allow you to change the script without any access to HTTPS keys.
> However, I suspect that ideal often isn't actually obtained; offline code signing can be a pain for CI.
You're probably right about this, but this is a great example of why you shouldn't use the same key for both code signing and HTTPS.
Every distro has a keystore / certificate store to which root can add whatever keys/certificates they like. The only difference is in which keys/certificates are present OOTB.
> I don't think this is necessarily the case. Even if you gain access to the server, HTTPS keys are usually stored in such a way that only root can read them, right? So if you only compromised a non-root account, you wouldn't necessarily be able to read them (I might be wrong about this, I don't do this kind of thing professionally).
The purpose of the key is to vouch for the integrity of the data. If I can change the data the key vouches at will, I've effectively compromised the key, even if I never gain access to the bytes of the key itself.
> You're probably right about this, but this is a great example of why you shouldn't use the same key for both code signing and HTTPS.
If the CI pipeline has privilege both to (1) sign stuff with the code signing key (2) upload stuff to the HTTPS server, then even if the code signing key and the HTTPS key are different keys (which is the norm), compromising the CI pipeline is enough to effectively compromise both of them.
If something goes wrong, you can’t examine the script afterwards to help figure out what happened and how to recover.