[ultra-pre-RFC] Client Certificates for Cargo instead of shared tokens

Cargo should use TLS certificates instead of tokens. there’s no “prying eyes” issues and there’s built-in expiration, and by allowing multiple client certificates for the same account you still get all the benefits you have today.

this is just a simple way to prevent account/crate hijacking. we already have all the libraries we need for it, both on the crates.io end and the Cargo end, so it’s just a matter of using them to their full extent.

oh yeah and ofc the client has full control over expiration. so they could provide something valid for 100 years and that’d be fine.

Can you elaborate?

Why is it better in this respect if the crates.io client stores their private key on disk, compared to storing their login token on disk?

  1. not sending easily-cloneable login tokens over a network
  2. not displaying easily-cloneable login tokens to “prying eyes” (crates.io literally uses this term when you go to create a new token: “When working in shared environments, supplying the token on the command line could expose it to prying eyes. To avoid this, enter cargo login and supply your token when prompted.” (they do also completely ignore the issue of someone looking over your shoulder, because security is useless.)
  3. the above prevent leaking the token. it may be hard to recover the token from the above leaks but it’s much easier than breaking a public key. public keys were literally designed to be wide-open, unlike these “token” things.
  4. built-in expiry keeps an attacker from being able to use it if they happen to break it after the expiry.
  5. using an existing technology makes it harder to get it wrong.

No amount of security can fix this, when they say prying eyes on crates.io they mean people looking through your console logs.

1 Like

I don't think that's true. My SSH private key literally never appears on my screen, neither when I generate it nor when I copy my public key into some web form to establish its use for authentication, so it is entirely safe against someone looking over my shoulder.

Tokens are slightly better than passwords, but they still follow the principle of giving away a secret to prove that you know it -- which, if you think about it, is kind of crazy, because a secret given away is not a secret any more.

2 Likes

Please keep it professional, even in the titles.

9 Likes

I support the concept, but please consider using SSH keys, which many developers will already have and know how to manage, instead of TLS client certificates, which are unfamiliar and have poor tooling by comparison. Cryptographically, they are both container formats for a standard public/private key pair, so there should be no practical difference in security.

4 Likes

Mutual TLS is great for service-to-service authentication, particularly when no requests to a service are allowed unless the client principal is recognized, however I’m less enthusiastic about the idea when the client is a human, or when much of the API will still, by necessity, be accessible without MTLS.

API tokens are easier to work with for both clients and developers, can permit flexible AuthZ policies, and can be bound to a TLS channel using token binding.

I think some more valuable work than attempting to switch to MTLS would be to better protect crates.io tokens on disk (e.g. encrypting them under a password, and prompting for a password when they are used).

1 Like

don’t worry about the disk just yet. there’s a whole other host of problems that would be solved by certs: expiration, exchange/secretness, not leaking the thing to whoever’s looking over your shoulder, etc.

additionally, yubikey can do certs. it’s generally unnecessary but some ppl insist on having this available. so we should make it available.

IRC has been using CertFP for years now. learn it from them.

(what the hell is MTLS? I’m only talking about, literally, self-signed client certs.)

SSH keys don’t expire so they’re useless.

On the contrary, I think improving the API token storage situation would bring a lot more immediate benefits than changing the authentication method.

Signing with a Yubikey is performed by the CCID API and is in no way tied to the usage of X.509 certificates. Using a Yubikey in PIV mode requires use of their PKCS#11 library, ykcs11, which to put it bluntly is a giant pain in the ass.

If the CCID interface were used directly (which is possible in pure Rust, and something I've considered working on. I've already made a pure Rust YubiHSM2 library which is similar), it could be used for things like data-at-rest signatures on released crates (at least, without using other horrors like CMS). To me crate signing would be a lot more valuable than PIV authentication.

Mutual TLS authentication. It's a fancy way of saying "client certificates" (in addition to the client verifying the server's certificate)

3 Likes

API tokens are still a security exploit in and of themselves. I’m not even gonna call it a “vulnerability” at this point because it’s worse than that.

(same for password fields tbh, but w/e)

you’re using HTTPS, so use client certs. it doesn’t require you to DIY your own crypto and TLS over HTTPS (i.e. TLS over HTTP over TLS) is just… well. yeah. don’t do that.

use the things you already have! don’t overcomplicate everything! I know rust loves reinventing the wheel but sometimes it’s just straight-up dangerous to do it and you should use someone else’s wheel instead!

The pattern of "I give you a secret over an authenticated channel, then you parrot it on an unauthenticated channel to gain authentication" isn't new, though. It is, in fact, a common practice. Expiration and revocation of tokens is done by the server. It is, in effect, just a password that the server generated.

You can rightly complain that this isn't the best way to do it. However, it wasn't invented here.

Could you please write little instruction for users - how to use your idea instead of tokens. For example, I have no idea what are you talking about. I just want to compare lengths of instructions.

1 Like

Heroku doesn’t support authentication of client TLS certificates, so I don’t think this would be possible with our current infrastructure.

so you’d just run cargo certfp and it’d just put out the certfp in a format accepted by crates.io, generating the needed cert if you hadn’t done it before.

--options would allow you to configure stuff like key type (default to Ed25519), expiration (default 5 years), password-protection, whether to use a yubikey instead, etc as desired

then you just copy the certfp and paste it on crates.io.

1 Like

Please review token binding (RFC 8471) which I mentioned earlier. It provides a cryptographic mechanism for binding API tokens to particular TLS channels, effectively an automation for creating a local client-side certificate which a given token is contextually confined to.

Frankly I think the benefits of token binding are debatable, but it's potentially worth considering. I only point it out as an alternative to MTLS. Token binding was specifically created to address the security concerns you bring up, while also providing a sort of trust-on-first-use model for self-provisioned certificates which don't rely on a central PKI infrastructure.

Have your tokens and client certs too, in a way that can be retrofitted into an existing token-based system.

remove tokens completely.

except “hardware tokens” (aka private keys).

Removing tokens completly would break everyone using cargo to publish today, and it's also a breaking change for the recently stabilized alternative registries API.

4 Likes

so deprecate them first I guess.

the alternative registries are still useless at this point in time anyway, since there’s no federation/caching proxies.