Summary
Cargo's current system of having the registry token
sent verbatim in the Authorization
header is too flexible and makes it too easy to build insecure registries. This RFC restricts the form that the registry token
can take to either JWTs or asymmetric tokens. For compatibility, the restrictions only apply to authenticated private registries using RFC#3139.
Motivation
The Cargo team is concerned about stabilizing RFC#3139 as-is, since it allows private registry implementations to send any value in the Authorization
header, which can lead to an insecure registry.
RFC#3231 defines a completely new authentication scheme using asymmetric tokens that is significantly more secure but is difficult to integrate into existing authentication systems. To support these existing authentication systems, Cargo should also support short-lived tokens. In particular, Cargo should be compatible with systems like GitHub OIDC to allow CI builds to publish crates to a private registry without using shared secrets.
As a compromise between allowing arbitrary tokens and requiring asymmetric tokens, this RFC proposes allowing JWTs to be sent in the Authorization
header in addition to asymmetric tokens. JWTs are used by multiple large identity providers and can have short expiration times that make them less dangerous if leaked.
Guide-level explanation
Tokens for private Cargo registries must be either asymmetric tokens as defined by RFC#3231, or JWTs (JSON Web Tokens).
Cargo will validate that the token for a registry is a JWT by inspecting the header portion of the token. The expiration claim must be set so Cargo can validate that the token is not expired and has a validity period of less than XX days.
Reference-level explanation
Cargo will restrict the forms of tokens used by authenticated private registries (as defined by RFC#3139) to either JWTs (JSON Web Tokens) or asymmetric tokens.
The validation of the token will be performed when Cargo detects an authenticated private registry by either the presence of auth-required: true
in config.json
or the registry sending an HTTP 401
when accessing config.json
(for sparse registries).
If the registry is configured to use asymmetric tokens as defined by RFC#3231, the request can continue. Otherwise Cargo will validate that the token as a JWT.
To validate a JWT, Cargo first remove the Bearer
prefix from the token. The remainder of the token will be parsed as either a JWS (JSON Web Signature), or JWE (JSON Web Encryption).
For JWS, the token must be of the form: [header].[payload].[signature]
. Cargo will decode the header portion as a JSON object and validate the following:
- The
typ
isJWT
- The
alg
must not benone
.
Cargo will validate the payload portion as follows
- The
exp
claim must be set to a date not more than XX (subject to bikeshedding) days in the future. - The
nbf
claim (if present) is set to a date in the past.
For JWE, the token must be of the form: [header].[key].[iv].[ciphertext].[tag]
Cargo will decode the header portion as a JSON object and validate the following:
- The
typ
isJWT
- The
alg
must not benone
- The
exp
must be set to a date not more than XX (subject to bikeshedding) days in the future. This claim must be replicated into the header so that it can be decoded by Cargo.
Cargo does not perform any cryptographic validation of the token.
Drawbacks
This restricts the tokens that Cargo will allow for private registries. Registries that have existing authentication systems that are not based on JWTs will need to either migrate to JWTs or asymmetric tokens.
Requiring short expiration times means users will need to rotate tokens frequently or use a credential provider to generate them.
Rationale and alternatives
This proposal is fundamentally a compromise between allowing the token to be any value, and requiring registries to use asymmetric tokens.
Alternatives considered include:
- Stabilize sending any token to a private registry. We can stabilize a supported asymmetric scheme when it's ready. Pro: fast and every registry happy. Con: registries can do insecure things.
- When RFC#3231 is ready, stabilize requiring its use to use RFC#3139. Pro: does not allow insecure registry implementations. Con: large registry providers unhappy, not great support for GitHub OIDC.
- Redesign RFC#3231 to use JWTs and be compatible with GitHub OIDC. Con: not a small project.
- Require that tokens for private registries come from a credential provider. Pro: ensures long-lived tokens are more securely stored. Con: doesn't address the registry storing the tokens insecurely.
Prior art
NuGet, NPM, Python, and Maven all allow long-lived tokens to be used. NuGet and Python both support credential providers to generate short-lived tokens automatically.
Unresolved questions
How many days is reasonable for expiration?
Should token formats other than JWT be allowed?
Future possibilities
Since this design encourages short-lived tokens, users will need to be able to easily generate them. For CI pipelines, there is usually a token already available. However, for developer machines, users will want to set up a credential process that can generate the short lived tokens from their identity provider. The credential process feature is currently unstable and could be extended to better support generating short-lived tokens.