Depending on the compromised crate(s) the impact of this could be RCE across a very large number of developers, as well as the ability to backdoor consumer code.
We have seen this attack in the wild against NPM.
In this case an attacker was able to compromise a single package, leveraged that to steal npm credentials, likely with the hope of further leveraging those credentials maliciously. It is believed at this point that the victim developer’s account was using a password that they reused from other sites - an extremely common problem.
This attack is practical - in terms of effort it is very similar to any other attack against passwords, but with a far greater impact (these accounts can be leveraged heavily).
I want to be clear that none of my suggestions would be consumer facing. You can not badge account 2FA state - that is very dangerous. This is about making users safer, not making them feel safer.
Implement 2FA for the
cargo publish command.
cargo publish only utilizing a token, when the command is called you will need to auth with some out of band code.
edit: 2FA should also be required for token generation.
Make 2FA for new accounts mandatory
Coverage for 2FA is more important for this scenario than for a typical password authenticated service due to the ability to leverage the account so heavily against consumers.
Therefor, I propose that 2FA be made mandatory.
Rust is a small, but growing community. It will not necessarily be viable to migrate to mandatory 2FA later - starting the process now will make a significant difference going forward.
Before a new access token can be generated the user will need to set up 2FA.
This should simplify things - new users are already onboarded into the new system, and they can’t get a publishing token until it’s set up.
Cargo publish goes from:
-> perform 2FA action
This should be a very slight inconvenience.
CI/ Automated pushes
For automated pushes a human being involved is not viable. I propose that a separate key be used for these systems, and then it is up to publishers to manage that key.
This is already fairly standard for automation tooling - signing binaries, passing tokens for other tools, etc, is all part of existing workflows.
I don’t want to go into details on what this would look like, but basically something like:
cargo publish --ci-key=<secret>
secret is a generated token that signifies ‘I don’t need to 2fa’ to crates.io.
Multi-phase rollout of mandatory 2FA
When 2FA is provided as an option, cargo will need to be updated to support it. When rollout coverage is sufficient, begin phasing out non 2FA enabled systems.
A large number of accounts will not have 2FA. When these accounts attempt to publish:
a) An increasing delay (sleep) should be added b) A warning should be printed
Eventually, a deadline will be specified where accounts not 2FA’ing will not be able to publish until 2FA is enabled.
This could take quite a while, but getting to this state is ideal.
If this state were achieved, an attacker could not publish malicious code on behalf of a publisher, without also compromising their second auth factor.
Opt Out 2FA
Instead of mandatory, we could have it be opt-out. As in, you must explicitly say “I do not want to use 2FA”.
I believe that, given this choice, most people would choose not to set up 2FA, opting to just get their code pushed, and maybe coming back later. This is almost as bad as opt-in.
Again, the impact needs to be considered here. When a user chooses not to protect a typical online account with 2FA that decision only impacts them. That is not the case here - the lack of 2FA impacts consumers.
If opt-out is considered, I highly recommend: a) Very seriously warning users against opting out b) Repeatedly reminding users to opt back in
Opt In 2FA
This is just a worse version of Opt Out. I would bet that users overwhelmingly do not opt into 2FA.
I do not have good data for this (I imagine companies are reluctant to publish), and this may not be the case for more technical users, but I expect 2FA adoption to be quite low.
Consider the attack against npm. That attacker could have gained access to many, many tokens. Opt-in would have been significantly less useful, as they could have probably found unprotected accounts.
It’s not bad, but it’s not good.
Rust is much, much smaller than NPM. The impact of mandatory enforcement will be considerably smaller. There is a great opportunity here to not just do a little bit better, but to do what every security team wishes they could do - enforce 2FA.
Account Lockouts, 2FA recovery/ management
2FA opens up the opportunity for an attacker to compromise an account, set up 2FA on it, and lock the user out. There has to be some recovery mechanism here - but I think that’s out of scope.
Impact on devs with slow internet, no phone
I think a fallback to email is a viable approach here?
2FA on other actions
It may make sense to 2FA revocation or other actions, I have not considered it.