Thou shalt not trust thy neighbour's password
Posted 2016-06-24 19:20:05.000000
Last month I retweeted Eric M.J.’s announcement of Hex v0.12.0. The feature that caught my attention most of all was the addition of package checksums to
mix.lock. As I mentioned in my tweet, I had to cut the section on dependencies from my ElixirConf.EU talk due to time constraints. Let’s try to rectify that here.
npm-gate and typosquatting
Before diving in, let’s look at two recent incidents involving package managers that you may have heard about.
More recently there was the revelation that a student was able to get thousands of developers to run code that could easily have been malicious, simply by uploading packages with names similar to popular dependencies.
Both episodes got quite a lot of (mainstream) media coverage, for instance at Ars Technica.
The weakest link?
Those two stories illustrate a few important points about software dependencies:
- Adding a few package dependencies to your project can quickly lead to your relying on developers you may have never heard of, and their whims, skills and security practices.
- With a few simple instructions, and often with very little scrutiny, developers pull in code that is then deployed on production servers, or is run on developer PCs or build servers on corporate networks.
Think about this for a minute: while you are busy hardening your server’s Linux configuration, tuning your load balancer’s TLS settings and keeping your certificate’s private key safe, the weakest link in your security posture might be some unknown developer’s password hygiene. What if some developer somewhere used the same credentials for GitHub as for that dating site that was hacked last week?
Elixir, Mix and Hex
This is of course a generic issue that applies, to a greater or lesser extent, to all programming languages. So what does it mean for the Erlang/Elixir ecosystem and the Hex package manager?
There are a couple of things you should know:
In Elixir, compiling code means executing code: this is an inherent part of the language design. For instance,
defmodule takes a
do block that is executed at compile time to produce that module’s BEAM binary. This means an untrusted dependency will have an opportunity to run code not just on your deployment machines, but also on your build server and/or your laptop, where it could potentially access your (corporate) network.
Keep in mind that many Mix tasks automatically trigger compilation, e.g.
mix ecto.migrate and
mix app.tree, as well as
iex -S mix. In practice, after you’ve pulled in new or updated dependencies you should review them before you run any further Mix tasks.
Remember that the
mix.exs file that specifies a project’s dependencies is also code, which means this file gets evaluated as part of the
mix deps.get task. I’m assuming you don’t inject malicious code into your own
mix.exs file, but what about your dependencies?
If you pull in dependencies through Git or using a path reference,
mix deps.get will recursively evaluate the
mix.exs files of those projects. In case of Git this could potentially lead to unwanted execution of code that was just downloaded, before you had a chance to review it to decide whether it is safe to compile!
Update: I posted a question to the elixir-lang-core mailing list to collect feedback on ways to mitigate this risk. I’d love to hear from you.
The good news is that Hex instead uses package metadata to determine further dependencies that need to be pulled in. In other words, the
mix.exs file of Hex packages is not evaluated during
Hex ignores any non-Hex dependencies a package might have, so there should be no surprise code executions from Git repositories while fetching Hex packages.
Hex package integrity
There is currently no support in Hex for signed packages, but the metadata for each package does contain a SHA-256 checksum of the package contents. During package installation the checksum over the downloaded files is compared against the checksum in the hex.pm registry, and any mismatch causes Hex to abort.
Since the registry data is retrieved over HTTPS and is itself protected by a signature, the checksum provides reasonable assurance that the files you just downloaded match the ones submitted to hex.pm. The weakest link here is, once again, the original developer’s credentials.
This is where the checksum in
mix.lock comes in: any unexpected modifications to the package contents after you’ve locked in your dependencies will be flagged. So while you should still take extra care when adding or updating dependencies, there should be no nasty surprises when fetching locked-in dependencies during deployment.
In order to protect yourself from the risks described above, disconnect from the network and only code in machine language using a hex editor you wrote yourself. And don’t forget to wear your tinfoil hat.
For the slightly less paranoid:
- Make sure you understand when and where third party code gets executed.
- Make sure you do some basic due diligence on any dependencies you introduce into your project, including any sub-dependencies, both new and updated.
- Create a sandboxed build and test environment: this could be a VM or a container that isolates the project from the host filesystem and that only allows access to specific network resources (e.g. hex.pm); ideally, any policy violations would be logged, so you can investigate suspicious activity.