Requiring 2FA to Publish to Crates.io

I believe they have already stated that it was password reuse on other, compromised sites. **

It is possible that 2FA was compromised (and they chose to omit this), but doubtful. Beyond that, I don't think it's relevant - 2FA is already "the solution" to password compromise, and it's chosen by many, many companies.

I'm going to update my first post with something a bit more formal.

**

The maintainer whose account was compromised had reused their npm password on several other sites and did not have two-factor authentication enabled on their npm account.

2 Likes

Iā€™ve updated my first post to clarify some of my points. I apologize for the rough first pass - I was at work and did not have much time.

I hope this adequately explains my position on this, and addresses some concerns that others have raised.

I appreciate the discussion so far.

I feel like it kind of seems like Iā€™ve been stonewalling a bit, so I want to be clearer about my feelings, which are moderately pro 2FA (though I donā€™t see it as urgent):

  1. I think optional 2FA is a very good idea. Iā€™m uncertain the best way to integrate it into our existing flow.
  2. I think login tokens should probably also expire after a certain amount of time.
  3. I think requiring 2FA for all users is a bad idea, but I think we it wouldnā€™t be a bad idea figure out a ā€œthreshold of centralityā€ beyond which crates.io should require 2FA. Something like: if your crate is one of the X% most downloaded of the last 6 months, you need to have 2FA enabled.
7 Likes

I'm proposing something fairly significant, I would expect healthy pushback and exploration. Don't worry about it.

Perhaps I sound too urgent - while I do believe strongly that this is the right path, I am not trying to make it sound as if the sky is falling. But I do want to express that I consider this to be a reasonable approach to a real threat.

I actually don't think login tokens are that scary - at most an attacker can revoke and DOS. Expiration seems reasonable, but unrelated to this threat.

edit: Ah, actually, token generation should certainly be behind a 2FA - forgot about that.

All this means is an attacker would aim below the threshold. Determining that threshold seems complex (differentiating between bors downloads and human seems hard), and I don't think it's worthwhile.

Can you explain why you believe that 2FA for all users is bad? I've spent some time justifying why I think it is not bad.

This seems like a pretty good idea. It's not enough by itself, IMO, but it does throw up an additional roadblock, especially in the case of an abandoned or infrequently updated crate. In particular, this helps mitigate an attack on a maintainer who may not be paying as close attention.

Yeah, explicitly or implicitly revealing ideal crates to target seems pretty undesirable...

Security is often at odds with convenience. Personally, I'm pretty willing to be inconvenienced in exchange for having more assurances of security... so, with that out there:

The amount of software that I (and probably most of us) download and execute sight unseen freaks me out on a regular basis. :grimacing:

With rust's slant towards a slim std, crates.io is an enormously critical component... I think, in a way that leaves a language like Go less exposed. (ok, perhaps that's just because of Go's lack of dependency management forces widespread vendoring. which of course has its own, different security issues. but I digress...)

I think mandatory 2FA, or something like it (or several things like it!) would be wonderful. I think crates.io should be thought of as a community resource that we all, collectively, need to look after -- depending on individual people to opt-in and make that security/convenience trade-off, IMO, does us all a disservice. We all make mistakes, and aren't that great at estimating risk.

On the one hand, we are literally talking about making it harder to publish a crate... :slight_smile: ...and yet, it sure would be nice to only make it hard for the nefarious actor. So, some other ideas that spring to mind:

  1. Permit other 2FA options: email, sms (probably more trouble than it's worth).
  2. Publish to staging area, released by email/link challenge.
  3. Publish to staging area, released after some time delay.
  4. Also there's a link to say "hey wait a minute, I did not publish that crate, lock it down"

Some combination of the above? Something else entirely?

Despite my apparent love for making it more difficult to publish a crate, I think it's really critical to stay as inclusive as possible. Inadvertently causing groups of people to "not bother" because it's too annoying, or because they don't have a smartphone, or for other similar reasons would be pretty sad. (but more importantly, a loss for the rust community!)

4 Likes

A mandatory 2FA and some additional checks for 2FA-opt-out tokens would be a great improvement. Just make sure the options there are not limiting anyone - not because of inconvenience, but for technical reasons (lack of access to a smartphone, sms infrastructure, etc.).

One thing that I would like to suggest is not focusing too much on the details of the latest NPM accident and thinking more comprehensively: the core problem of NPM is the ease of pulling in code written by thousands of strangers into your projects. Itā€™s a hard problem, but requiring everyone to review every version of every dependency is not going to work, and any more scalable improvements would be great.

3 Likes

Having opt-out tokens for CI seems to defeat the purpose of 2FA. I use automated publishing for all my crates, I donā€™t think I even have an API token on my laptop. The only way someone can publish a compromised version of one of my crates is either via acquiring my GitHub login (where I do have 2FA) or acquiring the token from my CI. If I had an opt-out token also stored in CI then that would be acquired at the same time.

One idea I had for automated publish + 2FA is to have an extra intermediate step where crates.io has preliminarily accepted the new version but requires an owner to login and verify it via 2FA before it is publically released. Thatā€™s much more work than just having a 2FA gate during the current publish step though.

If it isn't on by default, nobody will turn it on by themselves. Yes, that's a tad hyperbolic. Only those who genuinely care about security in the large and at least know what 2FA is will turn it on. I'd expect that to be a fairly low percentage of the average programmer, unfortunately.

So, IMO it should probably be on/required by default, and there should be a (fairly painful/lengthy) way of opting out if someone decides that they absolutely positively don't want 2FA for some weird reason.

3 Likes

Thatā€™s why Iā€™m suggesting to make it required for owners of popular/important crates, who are high risk targets.

Mandating 2FA for every situation, even low risk, will make it more painful to get started with crates, and security absolutism may be putting new users off.

1 Like

Low friction alternative to 2FA, especially for CI:

  • Send confirmation email to crate owners. Include a temporary link there that unpublishes the crate.

Itā€™s of course not as robust as 2FA, but still a significant improvement, because email passwords generally are not as easy to steal as the unencrypted credentials file.

This would help detect when an unauthorized publication is made. It can be enabled immediately for all accounts.

It could be combined with welcome email with instructions/best practices for maintaining crates.

A stronger version could be:

  • Send email with confirmation link. cargo publish only uploads (so CI can do it), but the new version is not visible until the link is clicked.
8 Likes

This email could also include which API token was used to publish (I guess only in the email for the specific user that published in multi-owner crates), allowing the user to quickly revoke that token if they weren't expecting it to be used then.

3 Likes

donā€™t let users input passwords, just gen an xkcd-password (see xkcd-password crate)

then you likely donā€™t need 2fa

@Soni as far as Iā€™m aware crates.io does not store passwords anywhere currently. Logins are only via GitHub OAuth and cargo uses randomly generated API tokens.

oh right, cargo is only as secure as github. have you tried requiring github 2fa?

One trick might be to have 2FA on as opt-in for publishing, but have cargo throw warnings when itā€™s downloading a dependency for which 2FA is not enabled. That should provide pretty strong incentives to people who want their crates to be used to work with 2FA, but also allows early experiments to be on crates.io without needing to set up 2FA.

2 Likes

As a user (someone who just downloaded a crate with this warning), what would I be expected to do in this case? Track down the crate author and nag them to enable 2FA? This approach feels like it's relying on public shaming to influence behavior. It might be effective, but I'm not sure it's what we in our community.

4 Likes

fork the crate that fails to use 2fa, and run your own version with 2fa.

Some thoughts on this:

  • Cargo presently uses GitHub as an IdP, which is a great choice. This thread seems to be discussing adding some additional authentication factor beyond that to crates.io.
  • There are many potential 2FA methods that could be applied. npm uses OATH-TOTP for example. Requiring a TOTP to publish would have reduced the scope of the compromise, but any user entering a TOTP after being compromised is potentially vulnerable (e.g. via a keylogger). Where to store TOTP seeds is a tricky question.
  • If 2FA is added, I donā€™t think it should be mandatory, or have any effect on consumers of packages. It should be opt-in, and only affect the experience for individual user accounts. If Cargo users want higher security, that should come from cryptographic trust (i.e. package signing), not the authentication method used to publish packages.

Personally Iā€™m not a big fan of OATH-TOTP for several reasons. Iā€™d rather crates.io tokens be shorter lived and easier to obtain, e.g. through a browser-based authentication workflow that logs you into crates.io and obtains a token. This can do things like leverage U2F authentication vicariously via GitHub. Iā€™d look to the authentication flows used by Google Cloud Platformā€™s gcloud tool for a modern take on how strong CLI authentication can work, both for local use that can spawn/interact with a browser, or remote use that has a URI-based bootstrapping process.

I think itā€™s very important to consider automated crate publishing systems when discussing changes like this. Shorter credentials with an unobtrusive workflow are potentially a good idea for humans, but there should still be a way to get long-lived credentials for robot publishers, and also bypass 2FA mechanisms as they donā€™t make sense in that context.

9 Likes

Yes, having the opt out token means that a compromise of your CI is as bad as no 2FA. I don't think this defeats the purpose.

I think this is acceptable. If one wanted to not opt out for their CI builds they always could, and maybe supporting a workflow for that is worth considering, but I don't think it's necessarily worth optimizing for.

This just means attackers look at the download count before attacking a user. I don't like the approach for that reason.

No one has really justified this - since when is 2FA so painful? It's used on tons of sites, it's completely standard, and the world is largely considering how to make 2FA the default (and is mostly held back because they did not do it early, when it would have been simpler).

I can't ever support unpublishing of crates. The best feature of crates.io is that you can't unpublish. I would expect and hate to see this feature abused.

Passwords are unrelated. The issue is not a compromised password, but a compromisde token.

I address this in my initial post. Exposing account security details is not acceptable.

I also do not think consumers are in a good position to care/ understand these warnings.

I also think the complexity involved here (regarding transitive deps, etc) is far too high.

Correct.

Not sure I understand. The TOTP being out of band means that an attacker would not have compromised it, and could not have published.

Can you elaborate? Why is it tricky?

Can you elaborate on what this workflow would look like?

This is the suggestion I've described in my first post.

1 Like

The victim's computer is infected with malware, and the victim is entering a TOTP code into their compromised computer in order to publish a package. As soon as a keylogger sees the user enter a TOTP code it is compromised. Malware with a keylogger can steal it and use it to publish a package, potentially before a victim can even hit enter (or it can block/delay the user from hitting enter), meaning even if your TOTP system has replay protection the malware can still use the code first. Entering a TOTP code incorrectly is a common occurrence, so the malware can potentially get many codes by ensuring the user receives an error for an incorrect code so they enter another correct one which can also be stolen.

This is the tricky part about trying to defend against the threat of "victim's computer is infected with malware". It's something of a disaster scenario. However, requiring 2FA at the time a package is published is still useful because it means the token on the victim's computer isn't immediately exploitable, which is definitely a win.

(RE: Where to store TOTP seeds)

Right now crates.io relies entirely on GitHub as an IdP. There are no long-term authentication credentials and the current tokens can always be reset by OAuthing to GitHub.

Introducing another authentication factor changes that. If crates.io itself (i.e. the conduit app) is going to store these, it will be taking on a brand new responsibility it never had before: managing end-user identity credentials. Now there needs to be an entire provisioning workflow for TOTP, along with an account recovery mechanism for it (e.g. "recovery codes") in the event the TOTP seed is lost.

There are various other IdP/2FA-as-a-service options for managing TOTP credentials. They are mostly commercial products. The ones I like are aimed at enterprise use cases and probably not particularly applicable. I imagine the main criteria here for the Rust Project would be secure, reliable, and free, but where GitHub as an IdP for crates.io was something of a no brainer, using a vendor for this particular solution is much less clear cut.

crates.io presently issues long-lived tokens suitable for use in automations/services or directly by humans. This could potentially supplemented by a workflow which issues short lived (e.g. 24h) tokens to humans, in conjunction with some UX improvements to the authentication workflow.

Right now that looks like:

  1. Cargo prompts you to go to crates.io when you cargo publish
  2. crates.io gives you a UI for creating a new token
  3. Copy and paste token from crates.io into the cargo CLI tool

I'd suggest an authentication workflow that's identical to the gcloud tool:

Autodetect if the user is in a desktop environment or a remote shell. If the user is in a desktop environment, use something like the open crate to launch a web browser to complete the existing GitHub-based authentication workflow to crates.io, except provisioning a short-lived credential. Upon completion this can e.g. hit a localhost service with the new credential and install it completely automatically.

If the user is not running on a desktop environment or the browser is not otherwise open, display a URL the user can reach to complete the auth workflow. The CLI tool can poll for success, so upon completion of the auth workflow the user is no longer required to manually copy/paste the token.

The main advantages of browser-based workflows over e.g. entering a TOTP code into a shell are that they can leverage much stronger 2FA methods than TOTP, namely U2F or WebAuthN. These methods also generally provide a better experience: hitting a button is easier than transcribing a code. The devices also identify themselves with public key cryptography, making it easy to have multiple devices enrolled.

All that said, when we talk about crates.io publishers taking an extra authentication step to publish a crate, I think it's important to consider what that step actually accomplishes. I think a better thing that step could accomplish is unlocking a digital signature key used to sign a package. That key could be kept in any number of places: a password encrypted file, a Yubikey, a TPM, or something like the Apple T1 chip connected to the TouchBar. Using additional authentication to protect a signature key means you don't just get better authentication to crates.io, but you get authentication which any user of a crate can easily verify.

I think when we talk about changing the cargo publish experience, signing crates is what we should really be looking towards.

5 Likes