A growing number of organizations are delivering software, generally for macOS, with a very Unix-y but also controversial pattern: using curl(1) to download a script and piping the output directly to sh (a.k.a. Bash(1)). There’s even a blog tracking the phenomenon, with the pointed description, “People telling people to execute arbitrary code over the network.”
What’s so bad about the curl | sh pattern? It definitely has its risks, but if done responsibly, it’s probably not the most irresponsible way to get software. However, it’s also not the best. There’s a spectrum of risk at play, with many angles to consider. Like all good security analyses, these angles are best considered by looking at how we can attack various parts of the pattern.
Attacking the Transport
By far the most irresponsible use of curl | sh is to use it with plain, unauthenticated, insecure HTTP instead of HTTPS. This is because it’s not only possible, but also increasingly likely, that the connection over which the shell script is delivered could have its contents silently modified by anyone in network position between the vendor and the installer—especially if you’re using public Wi-Fi. (If you don’t believe this is a real risk, consider that some companies’ business models revolve around modifying JavaScript delivered over HTTP in-transit—and anyone can hijack Wi-Fi with inexpensive, easy-to-obtain devices.)
There’s a more subtle attack against the transport, as well: If you don’t check the TLS certificate that the remote server sends back, it becomes trivial for a hijacker to interrupt your HTTPS session and replace it with one of its own. If you’re not sure whether your use of curl or some other tool is checking certificates correctly, try it (without the pipe to the shell) against some of the cases at badssl.com. You should also consider the fact that the web server delivering instructions for the curl | sh may have any of the above issues, sneaking in a -k
to disable certificate checking.
With all of this in mind, transport attacks are definitely not limited to curl | sh. If you’re doing it old-school and pulling down a tarball, then running configure
and make
and make install
, then you have all of these same concerns to worry about, unless you plan to read the entirety of the delivered code (seriously?!) before executing its install script. Transport security is a concern for all software delivery mechanisms, and you should always be aware of the risks regardless of the method you use.
Attacking the Content
Now that we’ve addressed our transport concerns, we’re home free, right? Not yet. There’s still the serious concern that despite our assurance that the installer is talking to a known and certified software source, the bits of the software sitting on the web server itself are in question.
In an era where cloud compromises are increasingly common, you must consider the possibility that the software published by the development team today is not necessarily the software that the web server will issue up tomorrow. You need to ensure that the chain of custody from the developer to the user is cryptographically secure by signing that content. The best way to do this is to distribute the software through a channel that has an already-trusted third party establishing a relationship with and signing the signing keys of the developer, much like a CA signs the certificate for an HTTPS site.
However, it’s not always convenient or possible to achieve this, depending on the kind of software you’re distributing, so you may need to consider alternative methods. A simple secure hash (as of the time of this writing, SHA-256 or better) of the software that can be verified by checking it from an outside source (to prevent compromise of the software and checksum simultaneously) is already miles ahead of having no checksum at all. Signing software with PGP is theoretically even better, but absent a way for users to trust the PGP key you sign releases with, its value is questionable—and PGP tools are not nearly as likely to be installed as hashing tools are.
As a software distributor, you should be aware, of course, that many users won’t bother checking your hashes if you just post them in any old place. You should call attention to your hashes up-front in your installation process. Better yet, if you can get your software into something like Homebrew, you can call them out as the preferred method for installation and rely on the fact that they checksum your source code.
Attacking the Tools
The tools used in software distribution are less likely to be compromised than the above, especially if you exclusively use tools that have been delivered with the user’s operating system and (hopefully) that delivery has already been well-secured. If you’re doing this, you don’t really have to address this risk with your user.
But if you don’t use tools the user already has—perhaps because you want to encourage the use of PGP signatures—you should make sure you explain to the user how to obtain those tools securely. Any tools you direct a user to download are subject to all of the above risks, which can have the unfortunate effect of increasing a user’s overall risk–even if they feel they’re doing a better job of vetting your software.
Security Is a Spectrum
There’s no such thing as “totally secure” or even “totally insecure”—security in all things, including software distribution, occupies a spectrum. If you make sure you’ve secured the transport that delivers your software, and you have secured the integrity of your software distribution itself, you move up on the spectrum. But the risks of noncompliance (will your users do optional checks on integrity or end-run around your transport security?) and additional tangential risk (will the tool you’re asking people to use expose them to more potential problems?) can push you back down.
curl | sh isn’t intrinsically insecure as a pattern. If you’re aware of what can go wrong, you and your users will be better-equipped to wisely use it (or not use it!) and move yourself as far away from the insecure end of the spectrum as you can.