rubygems / rfcs

RubyGems + Bundler RFCs
45 stars 40 forks source link

Require MFA for most-used gems #35

Closed jchestershopify closed 2 years ago

jchestershopify commented 2 years ago

Introduction

MFA is an ongoing subject of work for Rubygems, and for good reason. Research has shown[0] that account takeovers are the second-most common form of attack on software package repositories (after typosquatting). As a class of countermeasures, MFA is the best single way we have to reduce the probability of account takeovers.

We are not alone in facing this difficulty. Most notably, NPM has had a recent high-profile spate of takeover attacks on popular packages, which were only detected because of errors in the embedded exploit code. Other cases may well be undetected in the wild.

Yesterday, NPM folks announced that they will make MFA mandatory for "a cohort of top packages":

... we will begin to require two-factor authentication (2FA) during authentication for maintainers and admins of popular packages on npm, starting with a cohort of top packages in the first quarter of 2022. We are currently evaluating next steps to ensure that the strongest and most user-friendly authentication options, such as WebAuthn, are available and accessible to developers using npm.

I think Rubygems should follow this precedent.

Some Implementation Questions

I recognise that it's not as simple as flipping a toggle, or even doable in a single PR. But I believe we should at least aim for this policy and take incremental steps towards it.

Some problems that need to be solved for this to become a policy:

Next steps

The team I belong to at Shopify would like to develop an RFC for this policy change. We are enthusiastic about raising the MFA bar for Rubygems and we'd be prepared to do a lot of the technical work in support of an accepted RFC.

[0] See Towards Measuring Supply Chain Attacks on Package Managers for Interpreted Languages:

We categorize malware by their attack vectors in Figure 8a, which shows that typosquatting is the most exploited attack vector, followed by account compromise and publish. [emph added]

duckinator commented 2 years ago

I think this is a really good idea, and I'm happy to help when I can (with the caveats that I don't know a lot about security-specific stuff and am only doing ~8 hours/month of work on RubyGems/Bundler at the moment).

Given that (as you mentioned) human intervention is typically viewed as undesirable for CI/CD, I think this winds up intertwined with rubygems/rubygems.org#2755 ("More restrictions on API keys").

Specifically, I think we will need the following for an RFC of this sort to fully resolve the problems it sets out to fix:

  1. Ability to limit API keys to specific gems.
  2. Ability to limit API keys to specific actions (e.g. push-only keys for CI/CD).
  3. Ability to disable MFA/OTP for specific API keys (e.g. for automated releases).

It's unclear to me how much these are intertwined with what you're suggesting, but I don't think we can realistically enforce MFA as a requirement until we allow automated releases despite MFA being enforced. Sadly, the only way I know of doing this is by adding an exception for automated releases, which needs 1 and 2 to minimize the damage in case a MFA-disabled key is leaked.

If you have any resources on how to do automated releases with MFA of some sort, I'd love to learn more about it!

jchestershopify commented 2 years ago

I agree that in the short term we need to provide a narrow-as-possible exception for CI/CD cases. We'll include a section in an RFC laying out those requirements as part of flipping on mandatory MFA.

In the longer term I have a vague sense that a two-step process will allow CI/CD systems retrieve single-use tokens from rubygems.org, which are then handed to relevant build steps. That way the business of authenticating can be separated from user-configurable build steps. In OAuth this is done with refresh tokens being used to retrieve access tokens -- the build could only get the access token, not the more-valuable refresh token.

Github provides something that appears similar but slightly different: they will attest a build's workload identity to cloud providers, who then provide a single-use token for that build. It appears to be distinct from the OAuth refresh token flow, and has the advantage that there's no long-lived refresh token to protect. The downside is that it only really works for CI/CD that's managed as a SaaS, because rubygems.org would need to explicitly trust the origin of the attestation about workload identity. It would probably be easy to cover Github and Gitlab with some polite nagging, but then there's a long tail of CI/CD SaaSes and private build systems that would be left in the cold.

jenshenny commented 2 years ago

+1 to improving and adding more restrictions to API keys outlined in https://github.com/rubygems/rubygems.org/issues/2755.

In parallel to enforcing MFA on most-used gems, I believe we should also start requiring (or at least encourage) new accounts to have MFA setup during the sign up process as for the long term, it would be best that all accounts have MFA enabled.

jchestershopify commented 2 years ago

Good point, we should frame it as "cases where MFA is required": top gems, new accounts and to create new gems.

qrush commented 2 years ago

Just wanted to voice my support of enforcing MFA especially for the top 100-1000 gems, and encouraging it on signup. I'm not sure if we need to require it for new users but maybe making it as part of the signup step will help.

Re "single use tokens", I like that idea too - I'd love to see that as part of the RubyGems.org API!

jchestershopify commented 2 years ago

Closing now that we've opened #36.