Skip to content

OIDC: Threat model for Warehouse #10644

Closed
@woodruffw

Description

@woodruffw

This is a summary of my own notes from conversations with @di concerning Warehouse's threat model around OIDC (and GitHub, in particular, as an OIDC provider).

JWT considerations

JWT reuse

Warehouse needs well-defined and well-documented behavior around handling of a JWT seen more than once. For example, a GitHub action might do the following (pseudocode):

mint a JWT

for each dist in dist/* {
    use the JWT to mint an access token
    upload dist with the access token
}

In this case, the JWT gets used N times: once for each access token minting. This is probably not what we want; instead, we want this:

mint a JWT
mint an access token using the JWT

for each dist in dist/* { ... }

In this use pattern, we invalidate the JWT in Warehouse's backend after its first use. The process for that is probably as simple as performing uniqing on the jti claim after JWT verification, and then adding (jti, exp) to some table, where exp is the JWT's expiration claim (which gives us the ability to automatically clear out old JWTs once they expire).

Non-JWT considerations

Access token ephemerality

We should determine exactly how long-lived our ephemeral access tokens/API keys will be: how long is a reasonable period to allow uploads for? Should we cap the number of individual requests at some high number (e.g., is there any legitimate packaging workflow that creates more than 100 separate distribution files and uploads them)?

Provider-specific considerations

GitHub: account resurrection/reuse

GitHub allows a deleted user's username to be reused. When this happens, JWTs minted by the new user are indistinguishable from JWTs minted by the old one. This is a potential problem if PyPI is configured to accept JWTs from user/repo @ workflow, where user changes hands on GitHub -- the new (malicious) user would still be able to authenticate with PyPI as if they were the old user.

We probably need a mitigation for this before we fully enable support for GitHub as an OIDC provider. Two potential solutions:

  1. Verify the repository_owner claim in the JWT with some sidecar information: on trusted setup, retrieve the GitHub User ID (which is hopefully unique) for the user and cross-check it against the current ID. This is not the preferred solution.
  2. Get GitHub to include a repository_owner_id claim in the JWT, or something similar (like a repository_owner_epoch, if they don't want to guarantee the ID's uniqueness).

Neither of these is a great solution, because both still require some amount of sidecar state: we still have to either initially store the GitHub user's ID during trusted setup or during TOFU, so that we can check for changes during subsequent authentications. That complicates our DB schema, which we'd like to keep generic.

GitHub: JWTs minted for other consumers on the same workflow

GitHub allows any action to mint a JWT. Warehouse's consumer will only accept JWTs with acceptable claims (e.g., a matching ref), but that might not be sufficient.

In particular, it's conceivable for a publish workflow to have two jobs: publish-pypi and publish-rubygems, both of which mint JWTs for their respective services. Currently, there is no way for Warehouse to distinguish between these two JWTs: both originate from the same workflow. We could distinguish them with the aud claim, which Warehouse would then filter on, but that claim does not provide authenticity: an attacker who manages to take over the publish-rubygems job could mint a JWT with aud=pypi. We should determine whether this is a situation we need to handle on Warehouse's side.

GitHub: access token leakage

We should make sure that any (official) actions that mint access tokens via an OIDC JWT add the access token as a "mask value" so that it does not appear in CI logs: https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#masking-a-value-in-log

GitHub: trusted workflows

Warehouse has no visibility into workflow access: a user could misconfigure their GitHub Actions such that anybody can run a publish/release action and thereby publish malicious distributions. We can't prevent this, but we should probably provide documentation/guidance to steer users in the right direction.

This is true also for trusted branches/tags: anybody who can push a new tag to the repo could conceivably authenticate with PyPI if the action is e.g. configured to match against tags like v*.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions