Personal website of Martin Tournoij (“arp242”); writing about programming (CV) and various other things.

Working on GoatCounter and moreGitHub Sponsors.

Contact at martin@arp242.net or GitHub.

This page's author

Piping curl to s(hell) claims that using curl example.com/install.sh | sh to install software is a “glaring security vulnerability”. I’ve seen this claim many times in other places as well, with strong terms like “malpractice”.

I don’t get it. You’re not running some random shell script from a random author, you’re running it from a software vendor who you already trust to run software. Are you going to audit all of oh-my-zsh? Probably not. So why give extra gravity to their install script? If you trust oh-my-zsh then there’s no reason to distrust their install script.

There is no fundamental difference between curl .. | sh versus cloning a repo and building it from source. Do you know what’s in ./configure? GNU configure scripts are tens of thousands of lines of unreadable autogenerated code. No one audits that and it’s the perfect place to hide nefarious commands.[1]

It’s worth pointing out that the install scripts for all the cited examples are in source control[2] and subjected to the same kind of auditing as the software itself. There is no hard guarantee that the file in git is the same as the one you download from https://sh.rustup.rs, but you also have no guarantees that https://rust-lang.org/rust-1.39.tar.gz is really the same as git, and I don’t see anyone calling this a “glaring security vulnerability” or “malpractice”.

Package managers are more secure due to checksums, signing, and auditing.[3] You should use a package if available (often it’s not, hence these scripts). curl | sh is certainly not a perfectly secure method, just no different than git clone && cd dir && make, and I don’t see anyone issue warnings against that.

The only real security difference I see is that curl | sh is a very direct way to run code from the internet, whereas the other methods are running code from the internet, but with extra steps. It may “feel” different, but in reality it’s just the same. In the end it’s still just running code you didn’t personally audit on your computer, and a matter of trust.


To break down the bullet points in the article in some more detail:

  • Man-in-the-middle attacks: this is only a problem “if the developers omit the usage of TLS”, as the article already mentions. This is increasingly rare, and all of the cited examples use TLS.

    Either way, it’s not a problem with just pipe-to-shell, it’s a problem with any code you retrieve without TLS.

  • Hidden text attacks: it’s possible to trick browsers in copying “hidden” text with some HTML and CSS trickery. This is known as “pastejacking”.

    If I trust docker.com enough to run dockerd, then why shouldn’t I trust their website to not inject hidden stuff?

    People are going to paste stuff in their terminals regardless (keys, passwords, code snippets, what have you) so this is best fixed in the terminal or shell instead of telling people to “not do that really convenient thing”. zsh has been doing this by default for years; and it looks like newer versions of bash do too.

    As far as I’ve been able to find, there has never been any real-world attack using this technique, so it almost becomes a philosophical question: “if there is a potential security problem and no one is exploiting it, then is it still a security problem?”

  • User-Agent based attacks: a server can send a different version to Firefox than curl.

    As with the previous issue: you’re already trusting the vendor and site, and you’re already going to run the software that install.sh downloads. No need for these kind of tricks.

  • Partial content: the shell may execute half the script due to a network error.

    Easily fixable by running in a function:

    do_work() {
       :
    }
    
    do_work
    

    All of the cited examples already do this.

  • Not knowing what the script is going to do.

    I tend to avoid these kind of scripts for exactly this reason, but this is a personal preference and not a security issue. (For reasons I don’t fully understand myself I am somewhat irrationally obsessed with keeping my system as “light” as possible, almost to the point of stupid light.)

    It’s fine to not like these scripts, but that doesn’t make them “glaring security vulnerability” or “malpractice”.

Elsewhere on the interwebs
  • The problems with piping curl to a shell are system management ones – I mostly agree with Chris; “trust” in the sense “doesn’t do anything malicious” is probably a safe bet, but “trust” in the sense “trust this script won’t do anything to my system that I would rather not have” is more tricky; where will it download stuff? Which files and directories are created? Can I easily undo whatever it does? How well does it deal with failure like some shell utility not being present? These are serious concerns, but different ones from security concerns.
Footnotes
  1. You can regenerate them from autoconf, but I don’t think many do. Even systems like FreeBSD ports just use existing configure scripts. 

  2. oh-my-zsh, calibre, docker, yarn, rust

  3. Not all packages are audited though. Anyone can publish a package on npm, Rubygems, Pypy, etc.