Why You Shouldn’t Trust Your Composer Dependencies

15th Dec '186 of your Earth minutes

You Shouldn’t Trust Your Composer Dependencies

I’ve got a little present for you: you’re enjoying a well-earned break from work! However, during your holidays, your mission-critical web app has been taken over by some nefarious third party and is being abused all over — customer data is being stolen, keys and passwords to internal systems are being leaked and your databases are being corrupted and erased in an attempt to sabotage your company’s business.

Granted, that’s a pretty extreme nightmare scenario, but it could be your reality when relying on dependencies coming from untrusted sources. We’re already seeing this potential with NPM.

If you’re a PHP developer, there’s absolutely no reason why this can’t be true of Composer dependencies too!

Let me be clear before we go any further: I am super thankful for Composer. I use it almost every day. I believe it has immeasurably changed the face of PHP development for the better. But we shouldn’t blindly trust the code that it serves us.

“Nope! Composer is safe!”

Sorry to burst your bubble, but Composer isn’t safe. It suffers from the same weakness all package managers do: it allows any error-prone and lazy human being with some coding ability to publish a package of unknown trustworthiness to a public catalog of packages without any guarantees around the security of their code.

As an added bonus, whether as some sadistic joke or maybe it’s just a fait accompli, packages can reference other packages that they rely on thereby creating a cascading dependency graph of fallibility all of which can be easily downloaded onto your server with a few keystrokes and a generous helping of total bliss.

What this means in practice is that code you rely on, written by people you ‘trust’ (in a hand-wavy way), can still be the unwitting Trojan horse carrying a sneaky Greek in through your back door (so to speak).

Adventure Time

So let’s say you like using Laravel. I like Laravel a lot. I have nothing against it, I’m not calling it out as specifically good or bad example. It’s merely for the sake of example.

So, Laravel. You decide to set up a Laravel app just like you have with every other Laravel install you’ve done. Before you know it, a whole heap of third-party code is making its way onto your computer.

Unbeknownst to you or Taylor Otwell, one of the deep dependencies of Laravel has been compromised by hackers. It turns out that the SSH key used by the creator of that package was leaked and the repository that acts as its source is now silently leaching a vulnerability to all of its dependents.

That means every new install of Laravel is compromised.

What’s worse is that it’s gone completely unnoticed because the developers of the exploited package haven’t touched it for a while and are totally busy with other things, plus no one externally has reviewed the code, no code analysis tool has run over it and no one has reported any issues — the hackers have left their tool dormant until they find a juicy target.

Before we go any further, I guess you’re probably thinking “Well this all seems very unlikely.” Right, so you already accept that it’s possible. I’m not talking about likelihood here, just possibility, and you’re right, it is entirely possible that this could or even is happening right now!

How are your nerves? Shall we take this a little further? Ok, so it turns out that this sleeper hack is an executable Vendor Binary script that gets symlink’d into your Composer vendor/bin folder.

This isn’t so bad, you figure, because vendor is never made public, no one can run code there unless they know what’s available and how to address it. But that doesn’t stop the package from running it if it can find a way to execute the code that will run the script.

Well, guess what: Laravel has Package Auto-discovery which opens up a whole bunch of ways to inject functionality into almost any level of your application through a Service Provider contained in any dependency at any level in the dependency tree.

The hackers have added some complex-but-inert-looking code into the package which actually registers a Laravel route (hiding itself from any CLI inspection i.e. it won’t show when running php artisan route:list) which exposes a URL to some of their code which will run this vendor binary.

Perhaps they’ve also found a way to dynamically inject tasks into the Task Scheduler so now they’re able to ping their own service endpoints to figure out which public apps are out there in the wild, ready to be exploited.

What they choose to do with that code is now entirely up to them. If you’re a good developer, you’ll keep your packages updated, so they can change their code as often as they like and have all sorts of different symptoms triggered on exposed systems.


An Acceptable Risk

Ok this is quite the gloomy story and for sure this is perhaps an overly pessimistic view of the Composer-package-creating community. I have a lot of respect for the security standards of many in the PHP community who are working tirelessly not only to protect themselves, but also to help others do the same.

But it’s widely accepted that installing untrusted third-party code (which is exactly what you’re doing with composer require x) is a massive security risk.

Still it seems that some in the PHP community feels that this is acceptable.

There are obviously some things you should be doing whenever you consider installing third-party dependencies directly, but for indirect dependencies we tend to put our faith largely in the developers of the intermediary packages to have done this job for us. No one forces them to. It’s still up to you!

What else can you do?

1"extra": {
2 "laravel": {
3 "dont-discover": "*"
4 }

All of these are great steps, but there’s no silver bullet here. If you need a dependency that is either compromised or itself depends on a package that is, your app is at risk.

Enter Ensemble

I’ve been building Ensemble specifically to aid in mitigating this and other vulnerabilities in your apps by giving you greater insight into the packages and versions that your apps are using, alerting you to when packages are updated. (Read more about Ensemble)

It is simplistic for now, but I plan to make it bigger. It’s been in public beta for a while but I haven’t had the resources to build it out further.

I need your support! Please try Ensemble out and give me your feedback. It’s completely free. If you think it’s something worth doing, I’d really appreciate your financial support too. You can support the development of Ensemble and the safety of your apps by becoming a sponsor. You’ll continue to get complete access to Ensemble and when we hit various targets you’ll be the first to have the opportunity to run a version of Ensemble yourself as I plan to Open Source it.

It could save your bacon one day!Please seriously consider helping to improve the safety of countless PHP apps out there and the Internet in general by using and supporting Ensemble. Thank you!

#notadesigner • #sometimesitworks

All content licensed CC BY-SA 4.0  •  Code highlighting by Torchlight