Code & CI · Secret guide

Leaked PyPI Upload Token: what to do in the first hour

Critical severityDetected onlyLast verified · 4 min read

A PyPI upload token lets whoever holds it publish new releases of your Python packages. If it is scoped to your whole account, it can upload to every project you maintain; if it is scoped to one project, only to that one. A malicious release installs on the machines of everyone who updates, which makes this a supply chain risk rather than a personal one. Remove the token first, then check what has been published.

Check your GitHub account for leaked secrets — free

Provider
PyPI
Severity
Critical
Impact
Source code and build access
Checked live by LeakWatch
No

What a PyPI upload token looks like

PyPI API tokens begin with the fixed prefix pypi-, followed by a long base64-like string:

pypi-AgEIcHlwaS5vcmc…XXXX   PyPI API token (masked, long, URL-safe base64)

The characters right after pypi- are the same for every token, because the token is a signed macaroon that starts by naming pypi.org. That makes the string easy to recognise. In practice the token is used as the password of an upload with the username __token__, either in twine, in a ~/.pypirc file or in a TWINE_PASSWORD variable. Do not confuse it with TestPyPI tokens, which have the same form but only work on the test index.

How PyPI upload tokens get leaked

  • .pypirc committed. The configuration file used by twine copied into a repository or a dotfiles project.
  • CI workflows. A token typed into a publishing job in plain text rather than stored as a secret.
  • Build logs. A verbose upload step echoing environment variables or the full command.
  • Docker images. A token passed as a build argument and left in an image layer.
  • Issues and chats. A token pasted while asking why an upload fails with an authentication error.

What to do in the first hour

  1. Find the token. Sign in to PyPI, open your account settings and go to the API tokens section. Each token is listed with its name, its scope and when it was last used.
  2. Create a new token scoped to a single project rather than your whole account, and store it in your CI secret store.
  3. Update the publishing job and test it on a release candidate or on TestPyPI if you can.
  4. Remove the exposed token from the list. After removal, uploads using it are refused.
  5. Check your releases. For each project the token could reach, open the release history and look for versions you did not publish, new files attached to an old version number, or recent releases at odd hours. If something is wrong, yank the release and tell your users.
  6. Review account security. Check the security history of the account for unfamiliar logins, confirm that two-factor authentication is on, and look at who has collaborator rights on each project.
  7. Consider trusted publishing. PyPI supports publishing from CI without a long-lived token by trusting a specific repository and workflow, which removes this class of leak. See also I accidentally pushed an API key to GitHub.

Not sure what else leaked from the same repository? Run a free scan.

Not sure what else leaked? Run a free scan.

Revoke it at PyPI

PyPI tokens are removed from the API tokens section of your account settings. Sign in as the maintainer who created the token, find the entry and remove it. Only the owner of the token can remove it, so if a teammate created it, ask them or remove their collaborator access to the project while you investigate. PyPI’s help pages describe tokens and trusted publishers. The interface may change, so if the wording differs from this guide, look for the section where API tokens are created and listed.

How LeakWatch detects it

The rule is called PyPI Upload Token. It matches the prefix pypi-AgEIcHlwaS5vcmc followed by at least 50 URL-safe base64 characters, up to a thousand. The shared opening makes false alerts rare. The severity is critical because of the supply chain impact. This type is not checked live: LeakWatch does not try the token against PyPI, since the only way to test an upload credential is to attempt an upload. A finding means the string was seen, not that it still works.

LeakWatch detects this format but does not check it live: it can tell you the key is exposed, not whether it still works. Assume it does until you have revoked it.

FAQ

Can someone replace an existing version of my package?

No, PyPI does not allow overwriting a version that has been published. They can upload a new version, which users who pin loosely will install. That is why you should check the release list and yank anything you did not publish.

Does a project-scoped token limit the damage?

Yes. It can only upload to the project it was created for, so the blast radius is one package instead of all of them.

My token was only in a private repository. Is removing it still needed?

If the repository has collaborators, forks or CI logs, yes. A token that publishes code is worth replacing at the slightest doubt.

Get alerted next time a secret leaks — create a free account

LeakWatch is not affiliated with PyPI.