Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Making popular Ruby packages more secure (rubygems.org)
189 points by tomstuart on June 13, 2022 | hide | past | favorite | 45 comments


This is great news! I like how the article cites evidence that MFA is disproportionately effective against account takeover.

If the rubygems devs are looking for other highly effective wins against supply chain attacks: I think the next thing is deeper support for lockfiles. Although Ruby has Gemfile.lock, it's not a true lockfile in the same way that package managers in the javascript/go/python ecosystems are. Specifically, locking versions is optional, there's no locking by hash (Github issue: https://github.com/rubygems/rubygems/issues/3379), and there's no capability to lock local or source-only dependencies by hash. By comparison: go modules, pipenv, npm, yarn, nuget, composer, and gradle already support locking by hash.


you can add cargo to that list as well.


This is a great first step to making dependencies more secure in the Ruby ecosystem. Congrats to the whole team for getting this done!


yes indeed. hats off to the people working hard to make things better for everyone in Ruby.


TFA truly was a blessing for security. Finally _something_ that can just be blindly mandated and actually works wonders. User training was not getting anywhere.


I applaud the move in the right direction, but please add support for webauthn. OTPs are really inconvenient in comparison.

It looks like maybe it's been in flight for a while? https://github.com/rubygems/rubygems.org/pull/2108


It's on the radar. While that effort has come to a halt, some of us would like to pick it up and get it over the line at some point.


I really wish more package managers added support for OIDC based authentication+authorization for package publishing. PyPi has an ongoing PR for this: https://github.com/pypa/warehouse/issues/10970 with some really great UX. You specify a repository name on GitHub and GitHub actions there get publishing rights automatically.

While 2FA is good, having a purpose limited JIT token for publishing packages is what will actually reduce risk. Otherwise, as it stands - PATs leaked from one project can be used across any of your other packages on all package managers.


Thanks for giving the OIDC work a shoutout! I'm also (biasedly) very excited about it.


Fantastic work by the RubyGems maintainers. Congratulations on the rollout, and please consider WebAuthn support in a future iteration!


As part of a team of maintainers of a popular (declining) gem, shame they don't make a mention of the extremely valid "gem is owned by a team, and anyone may push" model. I regret that the MFA token for many gems such as this may end-up in 1Password or similar, shared, along side the other credentials, rather than on a separate device or similar.


Gems can have multiple accounts able to push, and MFA tokens are per-account, not per-gem. So what's the issue exactly?


In fact, you can now scope API tokens per-gem as well: https://github.com/rubygems/rubygems.org/pull/2944


seems like the post you're replying to had already answered your question, in its second sentence:

> I regret that the MFA token for many gems such as this may end-up in 1Password or similar, shared, along side the other credentials, rather than on a separate device or similar.


Still not following.

"> I regret that the MFA token for many gems such as this may end-up in 1Password or similar, shared, along side the other credentials, rather than on a separate device or similar."

Emphasis mine. How does "the extremely valid "gem is owned by a team, and anyone may push" model" impact this in any way? Why would the MFA tokens need to be shared via 1Password if they are specific to an individual account?

Unless you're sharing the username/password for a master account between everyone with push access to the gem (which, I checked, Capistrano thankfully doesn't appear to be doing), there's no reason whatsover to share the MFA token, so it could happily exist on a separate device. And if you are sharing one username/password between everybody – don't do that. You don't need to do that to accomplish "the extremely valid "gem is owned by a team, and anyone may push" model". That's just a really stupid way to do anything.

GP seems to be thinking that everyone with push rights needs to share the same token, but that's simply incorrect.


As others have mentioned, such gems should have shared maintainership across multiple accounts (each with their own creds and MFA) as opposed to shared creds and MFA.


I'm having trouble parsing your post. You can add multiple owners to a gem. You can also disable 2FA for API access on a per-account basis (though it isn't recommended) for a CI runner--which, tbh, is how a popular gem should be being published. What's the objection here?


Throwing the black hat on for a moment surely I would just move towards the subdependencies of these popular gems (which realistically is where you would be targeting anyways I imagine) and can fairly reliably expect that my malicious changes get picked up upstream in due course.

Am I missing something here?


I see this kind of comment often, somebody implements a solid security improvement measure and a popular response is "what about x!?"

No, enabling MFA for the most popular packages won't end all security, but also your strategy of targeting subdependencies isn't very good, every dependency of a popular project will be more popular than its dependent parent.


Those dependencies will be downloaded more often than the “popular” gems, so they will be affected by the same policies.


They will be more popular!


Cool that’s what I wanted to clarify. Thank you.


One advantage (albeit by no means perfect) is that subdepdencies of Ruby gems tend to be much more limited than what you see in NPM (probably largely because you can only have one version of a gem running in an application).

Lots and lots of gems depend on Rails and / or ActiveSupport. There's probably a lot more "glue" gems that are widely used for stuff like HTTP clients. Beyond that though, your typical gem doesn't have an enormous amount of dependencies, and there's a pretty good chance that the dependencies of the top X gems are themselves in the list of top X gems.


I don't know the Ruby ecosystem, but do dependencies go by hashes? If so, an update to the dependency won't immediately affect the Popular Gem.

Sure, the maintainer could still naively update the dependencies and pull up a bad one during an update of the Popular Gem. But that Popular Gem update would have to happen after an attack on the dependency, and before the breach was discovered (assuming it has any way of being discovered before release of the popular package).


I'd imagine this would end up being rolled out to almost all users eventually. Getting the top 100 done is just the warm up.


Anyone know what happens to the people who won't activate MFA within the time-period? I'm guessing they'll be unable to publish, but still be able to login to their account to setup MFA, even after MFA started to become mandatory?


That’s correct. If you’re a maintainer of a very popular gem, as of 15th August you’ll no longer be able to e.g. `gem push` if you haven’t enabled MFA on your RubyGems account. You will of course still be able to log in and enable it.

More details in the RFC: https://github.com/rubygems/rfcs/blob/master/text/0007-mfa-r...


Thanks a lot for the quick clarification :)


Does MFA exists to force people to have/carry all the time smart phones or there's a way to use it without a phone? I mean in practice for repositories like npm or rubygems?


It’s around 20 lines of Python (with no third party dependencies) to write a TOTP generator, so no.


You need somewhere to physically store a secret, plus the ability to do some computation to turn that secret into a time-based one-time code. A lot of people do use their phone, but there’s nothing to stop you using a dedicated hardware token, or conversely just your computer (e.g. 1Password [0]) if you’re comfortable with keeping all your secrets in the same place.

Naturally there are security/convenience tradeoffs however you do it. The important thing is that, unlike with passwords, you never send the secret over the wire.

[0] https://blog.1password.com/totp-and-1password/#totp-isnt-the...


How about cryptographically signed packages as the next step? It boggles my mind that most popular package managers like npm, pip and cargo don't have verification of package authenticity before installing built in.


RubyGems does have gem signing, but it's not widely used.

There's a proposal for a new "one button" approach using sigstore[0].

Other ecosystems are also looking at sigstore too, and a lot of us are cooperating in the OpenSSF Securing Software Repos WG [1]. Package signing is a regular topic of discussion and there are various efforts underway.

Disclosure: I am involved with both of these.

[0] https://github.com/rubygems/rubygems.org/pull/2944

[1] https://github.com/ossf/wg-securing-software-repos


From [1]:

> RubyGems has had the ability to cryptographically sign gems since version 0.8.11. This signing works by using the gem cert command to create a key pair, and then packaging signing data inside the gem itself. The gem install command optionally lets you set a security policy, and you can verify the signing key for a gem before you install it.

> However, this method of securing gems is not widely used. It requires a number of manual steps on the part of the developer, and there is no well-established chain of trust for gem signing keys. Discussion of new signing models such as X509 and OpenPGP is going on in the rubygems-trust wiki, the RubyGems-Developers list and in IRC. The goal is to improve (or replace) the signing system so that it is easy for authors and transparent for users.

I tried it years ago and it was such a pain that I stopped, and I've a high tolerance for this kind of thing. I also found problems with certificate chaining that I've forgotten about (intentionally and gladly). Basically, it was like a lot of security stuff over the years, half-baked, badly implemented, with a poor UI and hence, doesn't get used.

[1] https://guides.rubygems.org/security/


PyPI supports uploading GPG signatures. Nobody uses it.


FWIW signed packages are available out of the box with rubygems: <http://docs.seattlerb.org/rubygems/Gem/Security.html>


Yes, but rarely used -- it's clunky. When I scraped the top 10k gems, < 1% had valid and up-to-date signatures.


Notably, signatures are not checked by bundler when installing gems.


I think HTTPS gets you pretty far. There are far more concerning attack vectors for those package ecosystems to me


Supply chain attacks are not uncommon. Package signing can protect against those:

1. Package source hacking. Say an attacker gets access to a package source's storage and inserts malware into popular packages. Now your project contains malware. Your package manager can detect tampering if the packages are signed.

2. Dependency confusion attacks. Say my project downloads packages from the public source as well as my company's private source. An attacker realizes my company has a private package named `private-foo` and uploads a malicious package with the same name to the public source. Now my projects contains malware as it occasionally downloads the malicious package instead of my company's private package. Your package manager can detect packages from an unexpected author if they are signed.

I can provide more examples if you'd like. None of these are hypothetical, all of them have happened already.


> Dependency confusion attacks

Just want to point out that bundler solves this problem (and many others). It pins gem versions in Gemfile.lock and it supports explicit source locations (like git repositories) for downloading gems.


In this setting, HTTPS provides integrity (the authoritative party is the package index, which can serve you anything.) It can't provide authenticity, which is the root of most of the more interesting attack vectors in packaging.


HTTPS only protects the transmission from repository to user. It doesn't protect the package in the repository.


Yeah but what trust system are you going to use for that? I don’t see how anything at scale buys you too much


The Linux distros use OpenPGP/X.509 keys stored on HSMs for their archive security, and a set of per-maintainer keys that are used to verify changes to the archive. RubyGems/Cargo/etc could use something like that.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: