Malicious remote code execution backdoor discovered bootstrap-sass Ruby gem
snyk.io
snyk.io
Ideas (shoot them down): - Somehow tie keys to location. - Require two-step verification for all commits for packages with over 1000 downloads. (Assuming their phone isn't compromised if the keys are).
https://reproducible-builds.org/docs/buy-in/
When it comes to verifying this, we need to have independent rebuilders that build these packages reproducibly, and can attest these rebuilds in a secure manner. I have been working with NYU Secure System Labs to try provide this to debian packages and we currently have a PoC for such a system.
https://reproducible-builds.org/docs/sharing-certifications/
https://ssl.engineering.nyu.edu/blog/2019-01-18-in-toto-pari...
But I think this goes in the right direction.
You will likely have to create patches for those piece of softwares that create a different artifact every time are built.
There are a few other challenges but in my humble opinion, it is worthwhile to strive to write your own software with "reproducible builts" in mind so everybody can verify if something was altered.
This is hard on distributions as we are suppose to support all the ecosystems. But for rubygems, pypi, go, rust, npm and so on this is a easier problem to solve if they want to. But even with all these challenges, Debian is at 94% reproducible packages, and Arch is edging closer to 80%.
Introducing non-determinism into your build process is something that should be removed, and all ecosystems should be aligned in this effort.
Weekly blog posts are written about the current efforts. https://reproducible-builds.org/blog/
Of course, this adds a big deliberate barrier to participation - newcomers have to get their key signed by an existing Debian developers. There are real security/usability tradeoffs, and at the moment people seem to have default-chosen "anyone can add packages really easily", resulting in a Cambrian explosion of packages.
A non-exhaustive list of things that could be done...
- Improve repository security (e.g. stronger authentication requirements). Problem here is most of the repositories are non-profit and this costs money. Also there's a risk that if you increase friction for developers, they'll go elsewhere.
- Require library signing. This has some potential benefits (may even have stopped this attack) but again increased friction for developers and management overhead for repositories.
- Curation of libraries. Actually pay some people to review the code in the libraries. This doesn't scale easily when you consider the volume that places like NPM have.
- Sandbox code modules
If a library doesn't need any permissions, and it's not granted any, then attempts to e.g. access the filesystem or network will throw exceptions. The JVM can do this, but it's not well documented how to use it.
Found the blanket statement for npm.
How about instead of reviewing 1 library of 10k lines that does everything, you review 10 focused modules for a total of 1k lines?
Also modules like left-pad don’t change that often so you probably have to review only changes in the top modules.
We need to stop this mentality that dependency count should be low. Look at total LOC and you’ll probably find that it’s not as bad as people like to paint it.
1) Signing should always happen. Publish time + hashes of external resources should be taken into account to prevent meddling with things after the fact.
2- a much harder solution) Don't use so many libraries.
In this way I think it's borderline impossible. Of course this vuln targeted `bootstrap-sass`.. it's basically a default gem for new rails installations since bootstrap is so commonly used.
Actually, rails 6.0 not only installs dependencies for you upon generating a new app, but it downloads yarn and installs 602 dependencies too.
It's a miracle we do anything as programmers.
But now you will move at the pace of a bacteria on a snail. But since the company will probably introduce vulnerability bugs in the code they write, it is probably not a very good idea.
We are using open source and free packages to save time and move fast. Security and speed/usability have tradeoffs.
The paranoid will do everything in their knowledge to protect themselves but they will move slowly. The naive will not do anything and sooner than later will get burnt.
Assuming you trust your current modules and you can trust that they will not be altered, you can lock down the dependencies. But now monitor and updating a library when a bug or vulnerability is found in a modules become another burden.
Those actors that have the budget, patience and the motivation will always find a way in.
I sometimes feel quite sad about the real situation but there is not such a thing as perfect security.
That said some of the suggestions I see here will dramatically improve the situation with a relatively small effort. 2FA and reproducible builds for example.
One that comes closer is vcpkg [1], which is the Microsoft-run package manager for native C++ packages. It works by having the list of packages and recipes to build them (which it calls "ports") stored in a single repository. All requests to update them are submitted as pull requests and must be approved by Microsoft to enter the main repository.
I'm not necessarily saying whether this is a good or bad idea. Certainly it is a very different situation: only libraries that are already popular are considered, often the ports are submitted/maintained by different people than the library authors, and writing a port to build a library on multiple platforms may require significantly more work than just pointing at an existing GitHub repo. I just point to it as an alternative idea that has had some success.
[1] https://brew.sh/
Just require 2fa everywhere. We're way past the point where password-only is enough if you distribute software.
Package distribution platforms could also ensure that your package comes from a tagged/signed commit in a selected repo. Reproducible builds could help here too. Finally package signing itself and possibly WoT for authors. (Where either the author has to trust the dependencies or you have to explicitly trust all of them)
So, not trusting third parties is the only way and with that language level capability based security can help. Although this is pretty far from common programming languages where any function can access any file, environment variable, global variable, constant, socket, etc.
Rails 6.0, when generating a new app:
- installs 102 ruby gems (without being asked)
- installs yarn & 602 yarn dependencies (without being asked)
The modern programming industry is a joke. We call ourselves "engineers" without any certification or oaths of responsibility, and basically ignore security beyond user credentials at best.
Someone please tell me `yarn list | wc` showing 2,800 transient packages is incorrect.
[0]: https://gist.github.com/azah/c219844f95243cdfdb3b352ad3dee2f...
And it is worse that what you just found out. Did you think about all the software and dependencies that are installed just to get to the point where you can install Rails 6 (ex: Linux)?
What about your coworkers? Are you sure that you can trust all of them and nobody will not sneak anything in?
If you are too paranoid on who and what you can trust, you may end up tossing all your electronic devices and go living in a desert island.
However, this does not mean we should by default be installing heaps of packages upon generating new packages. This is practically begging for a security exploit, particularly the JS packages.
[0]: https://www.archive.ece.cmu.edu/~ganger/712.fall02/papers/p7...
- Linux is more ubiquitous than Node.js. If somebody exploits Linux, they get at least one order of magnitude more machines. So there is a higher motivation.
- Funny code in C is harder to spot than in Javascript. Furthermore ...
- A binary is harder to inspect than any module written in Javascript (even if minified).
- The code in the Linux default installation has at least 2 orders of magnitudes of the code that yarn installs.
I was very conservative in my estimations.
Considering these kind of attacks, Linux seems more likely to be already exploited by a few organizations in multiple ways.
We have been trusting too much. As more code gets written and many more people come to learn to program, the number of supply chain attacks will increase. And at the same time, as the security know-how is easier to be accessed on the Internet, we will get smarter exploiters capable of hiding their wrong doing.
Just doing some eyeballing:
- These look like the 62 gems in the production group: https://gist.github.com/gkemmey/6d6fcd381596b1b355d16e1e3120...
- Of those 62, how many are first party (by rails or other trusted vendors)? Looks like the vast majority...
- These look like the other 16 (dev / test groups): https://gist.github.com/gkemmey/6d6fcd381596b1b355d16e1e3120...
- On the JavaScript side, rails adds 5 top-level dependencies. 4 of those 5 have one external dependency between them: https://gist.github.com/gkemmey/6d6fcd381596b1b355d16e1e3120...
Of course, the fifth is `@rails/webpacker` which has `webpack`, `babel`, and friends as dependencies. And the node ecosystem is what it is, but I think `yarn list | wc` is going to double count shared dependencies, so 2800 is high.
TL;DR - I think new rails is about as minimal a footprint as possible...
I'd like to know how they audit their system and the threshold of saying it is adequately secure.
I hope others copy this. This is truly scary.
Is there also a bundler-meta-audit the somewhere, so I can audit bundler-audit and it's dependencies? If not, maybe we should start a kickstarter for it?
This isn't going to go away, it's just a question of if/when the damage caused will get bad enough that people start to accept some of the tradeoffs that would be required to improve matters...
For commercially provided libraries that could be a contract specifying security requirements, with some specific measures of how to do that.
For Open source the only real option I can see is to curate your own package repositories and get a level of review that you're comfortable with. Definitely fixing versions and insisting on review before upgrade would help.
The problem with automated scanning is that they can't really find backdoors or just generally insecure code, they can find known vulnerabilities. They could use static analysis to find insecure code, but my experience of SAST tooling is that it takes a lot of manual effort to tune, it's not a pure automated scan option.
That's not to say that automated scanning provides nothing, but that it has limitations.
This is a non-starter. This is even worse than "just run your own mail server".
There's tools you can use to add automated scanning to your repo's and pin versions of packages, but that's doesn't really feel like curation to me...
companies like Sourceclear seemed to me to be going down this line but that doesn't seem to be the drive of their offerings any moe.
It also implies that you are compiling everything from source code or you trust the distribution channel of the binaries.
And you do it every time there is an update that you want to use. And you have a system that promptly alert you of vulnerabilities in libraries you use.
Unless you have a handful of modules that are already self contained, it is not practical for most teams.
Didn't this _also_ require a commit to the relevant Git repo? Or is it possible to upload a tarball directly to Rubygems without it being backed by a git repo?
> The backdoor was wisely hidden in the 3.2.0.3 version that was only published to RubyGems and no source of the malicious version existed on the GitHub repository and allowed remote attackers to dynamically execute code on servers hosting the vulnerable versions.
Basically all apps using package repo's (i.e. all of them) are relying on massive piles of unaudited 3rd party code with usually no idea of provenance.