# Require MFA on PyPI?

**URL:** <https://discuss.python.org/t/require-mfa-on-pypi/12077>\
**Category:** Packaging\
**Created:** [November 19, 2021, 12:37am UTC](https://discuss.python.org/t/require-mfa-on-pypi/12077 "2021-11-19T00:37:46Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![steve.dower](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/steve.dower/32/56_2.png) [@steve.dower](https://discuss.python.org/u/steve.dower)\
**Post date:** [November 19, 2021, 12:37am UTC](https://discuss.python.org/t/require-mfa-on-pypi/12077/1 "2021-11-19T00:37:46Z")

</div>

It seems [NPM](https://github.blog/2021-11-15-githubs-commitment-to-npm-ecosystem-security/) and likely [Ruby](https://github.com/rubygems/rfcs/issues/35) will require their most popular packages to enable MFA in order to publish new releases. This is a response to _every_ account takeover on NPM recently being for accounts without MFA, and they don’t want to risk it happening to anything that is widely used.

Possibly our top releasers all already have it enabled? I can’t get at that data, but if someone can and it looks like we’ve got good coverage already, then no big deal.

Otherwise, maybe this is something worth us thinking about?

---

<div class="post-metadata">

**Author:** ![fungi](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/fungi/32/5963_2.png) [@fungi](https://discuss.python.org/u/fungi)\
**Post date:** [November 19, 2021, 1:18am UTC](https://discuss.python.org/t/require-mfa-on-pypi/12077/2 "2021-11-19T01:18:28Z")

</div>

PyPI’s MFA is just for project management through its WebUI though,  
not for release uploads via twine, right?

---

<div class="post-metadata">

**Author:** ![cameron](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/cameron/32/2658_2.png) [@cameron](https://discuss.python.org/u/cameron)\
**Post date:** [November 19, 2021, 1:33am UTC](https://discuss.python.org/t/require-mfa-on-pypi/12077/3 "2021-11-19T01:33:43Z")

</div>

I have a bunch of (mostly) smallish interrelated packages. Every so  
often I have a little frenzy of releasing, and might update several  
packages. Am I going to want to get a YubiKey or the like to avoid  
having to hand type a code from my phone’s authenticator app for each  
push?

I’m not trying to scuttle MFA, I’m in favour of better security.

How feasible is a batch mode auth? I can imagine changing my flow to  
prep a bunch of releases, then do a single batch with a single MFA auth  
pass? Just pulling ideas out of the air here, of course.

Cheers,  
Cameron Simpson [cs@cskk.id.au](mailto:cs@cskk.id.au)

---

<div class="post-metadata">

**Author:** ![tiran](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/tiran/32/48_2.png) [@tiran](https://discuss.python.org/u/tiran)\
**Post date:** [November 19, 2021, 9:46am UTC](https://discuss.python.org/t/require-mfa-on-pypi/12077/4 "2021-11-19T09:46:58Z")

</div>

@fungi @cameron PyPI supports API tokens for uploads. You can generate an upload token with either user scope (useful for `~/.pypirc`) or single project scope (recommended for build pipelines).

---

<div class="post-metadata">

**Author:** ![tiran](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/tiran/32/48_2.png) [@tiran](https://discuss.python.org/u/tiran)\
**Post date:** [November 19, 2021, 10:00am UTC](https://discuss.python.org/t/require-mfa-on-pypi/12077/5 "2021-11-19T10:00:24Z")

</div>

> [@steve.dower](#):
>
> Possibly our top releasers all already have it enabled? I can’t get at that data, but if someone can and it looks like we’ve got good coverage already, then no big deal.
> 
> Otherwise, maybe this is something worth us thinking about

+1

We could start by blocking uploads with account passwords and user-scope token, and require either MFA login or a project scope token. This would stop people from putting their account password in their `~/.pypirc` or GHA.

IMHO Twine and PyPI should deprecate password logins for uploads and only support token logins for uploads to PyPI.

---

<div class="post-metadata">

**Author:** ![kpfleming](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/kpfleming/32/2761_2.png) [@kpfleming](https://discuss.python.org/u/kpfleming)\
**Post date:** [November 19, 2021, 11:22am UTC](https://discuss.python.org/t/require-mfa-on-pypi/12077/6 "2021-11-19T11:22:33Z")

</div>

This is what npmjs is doing as well; there are special-purpose tokens solely for uploads. Of course those can still be compromised, but it’s better than the current situation.

---

<div class="post-metadata">

**Author:** ![pradyunsg](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/pradyunsg/32/206_2.png) [@pradyunsg](https://discuss.python.org/u/pradyunsg)\
**Post date:** [November 19, 2021, 11:50am UTC](https://discuss.python.org/t/require-mfa-on-pypi/12077/7 "2021-11-19T11:50:11Z")

</div>

IIUC, this is something that we already want to do. There’s open issues on pypa/warehouse discussing this, and breaking it down into smaller more incremental changes:

- [Require API Tokens for upload if 2FA is enabled · Issue #7265 · pypa/warehouse · GitHub](https://github.com/pypa/warehouse/issues/7265)
- [Add ability to require 2FA for a given project · Issue #5792 · pypa/warehouse · GitHub](https://github.com/pypa/warehouse/issues/5792)

There’s other stuff related to tokens that we’d might want to resolve at the same time: [Issues · pypa/warehouse · GitHub](https://github.com/pypa/warehouse/labels/tokens)

I guess requiring it across the site for everyone, is not something that has come up on those issues, so… I don’t know whether pushing everyone to have 2FA before being able to upload is considered a worthwhile idea (the main concern being how do we roll this out, rather than should we do this).

---

<div class="post-metadata">

**Author:** ![fungi](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/fungi/32/5963_2.png) [@fungi](https://discuss.python.org/u/fungi)\
**Post date:** [November 19, 2021, 2:49pm UTC](https://discuss.python.org/t/require-mfa-on-pypi/12077/8 "2021-11-19T14:49:54Z")

</div>

This is exactly my point though. API tokens, while scoped, are still  
only single-factor authentication like passwords. So uploading will  
not require multiple authentication factors, only the WebUI will?

To be clear, requiring interactive MFA for uploading releases would  
be a disaster for automated release processes, so I’m simply trying  
to make sure nobody’s _actually_ proposing that. Thanks.

---

<div class="post-metadata">

**Author:** ![steve.dower](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/steve.dower/32/56_2.png) [@steve.dower](https://discuss.python.org/u/steve.dower)\
**Post date:** [November 19, 2021, 5:50pm UTC](https://discuss.python.org/t/require-mfa-on-pypi/12077/9 "2021-11-19T17:50:42Z")

</div>

> [@fungi](#):
>
> To be clear, requiring interactive MFA for uploading releases would  
> be a disaster for automated release processes, so I’m simply trying  
> to make sure nobody’s _actually_ proposing that. Thanks

I’m certainly not! But I wouldn’t rule out (the availability of) significantly shorter timed upload tokens (e.g. 1-30 days) that require MFA to generate. That way a leaked token is far less damaging than a 12 month, all access one.

> [@pradyunsg](#):
>
> There’s open issues on pypa/warehouse discussing this, and breaking it down into smaller more incremental changes

Perfect! These are what I was hoping to find, and apparently totally blanked on using “2FA” as a search term rather than MFA ☹

---

<div class="post-metadata">

**Author:** ![dustin](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/dustin/32/149_2.png) [@dustin](https://discuss.python.org/u/dustin)\
**Post date:** [November 19, 2021, 6:37pm UTC](https://discuss.python.org/t/require-mfa-on-pypi/12077/10 "2021-11-19T18:37:11Z")

</div>

> [@pradyunsg](#):
>
> IIUC, this is something that we already want to do. There’s open issues on pypa/warehouse discussing this, and breaking it down into smaller more incremental changes:
> 
> - [Require API Tokens for upload if 2FA is enabled · Issue #7265 · pypa/warehouse · GitHub](https://github.com/pypa/warehouse/issues/7265)
> - [Add ability to require 2FA for a given project · Issue #5792 · pypa/warehouse · GitHub](https://github.com/pypa/warehouse/issues/5792)

These are more about giving maintainers the ability to require 2FA for their projects collaborators, not PyPI itself requiring some subset of projects to use 2FA, like npm is doing.

I also don’t think the latter is something that we’re currently able to do: not for some technical reason, but because PyPI does not currently have a large support staff like npm/GitHub/Microsoft does. [Account recovery requests](https://github.com/pypa/pypi-support/issues?q=is%3Aissue+is%3Aopen+label%3Aaccount-recovery) due to lost 2FA are already a huge drain on staff/volunteer resources due to how time-consuming they are and their sensitive nature. A 2FA mandate at this time, without having support staff, would likely eventually result in an overwhelming backlog of requests.

---

<div class="post-metadata">

**Author:** ![brettcannon](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/brettcannon/32/34895_2.png) [@brettcannon](https://discuss.python.org/u/brettcannon)\
**Post date:** [November 19, 2021, 8:52pm UTC](https://discuss.python.org/t/require-mfa-on-pypi/12077/11 "2021-11-19T20:52:06Z")

</div>

> [@steve.dower](#):
>
> I’m certainly not! But I wouldn’t rule out (the availability of) significantly shorter timed upload tokens (e.g. 1-30 days) that require MFA to generate. That way a leaked token is far less damaging than a 12 month, all access one.

But time-scoping them like that will still break workflows if your project isn’t _that_ active. If I set up automated releases, but I only do a release a couple of times a year, then a month-long expiry on a token will kill that workflow.

I do agree with the sentiment of stopping accepting user passwords and account-wide tokens for uploads.

---

<div class="post-metadata">

**Author:** ![stoneleaf](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/stoneleaf/32/88_2.png) [@stoneleaf](https://discuss.python.org/u/stoneleaf)\
**Post date:** [November 19, 2021, 9:11pm UTC](https://discuss.python.org/t/require-mfa-on-pypi/12077/12 "2021-11-19T21:11:29Z")

</div>

The workflow may be automated, but surely kicking it off is not? The first step of starting an otherwise-automated process could be getting a new token.

---

<div class="post-metadata">

**Author:** ![tiran](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/tiran/32/48_2.png) [@tiran](https://discuss.python.org/u/tiran)\
**Post date:** [November 19, 2021, 9:12pm UTC](https://discuss.python.org/t/require-mfa-on-pypi/12077/13 "2021-11-19T21:12:58Z")

</div>

> [@brettcannon](#):
>
> But time-scoping them like that will still break workflows if your project isn’t _that_ active. If I set up automated releases, but I only do a release a couple of times a year, then a month-long expiry on a token will kill that workflow.

I agree with Brett. Time-scoping prevents some abuse, but it wouldn’t prevent immediate abuse of a token. If somebody gets hold of the project token for `urllib3`, then that person can do a lot of harm in a very short time.

Instead of time-scoping I suggest an **optional** two-step release process. In the first step automation uses a project-scoped token to create a new staging release and upload artifacts. In the second a human needs to log-in with MFA and press the release button. The workflow allows users to automate all the annoying bits and still require MFA for the actual release.

---

<div class="post-metadata">

**Author:** ![fungi](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/fungi/32/5963_2.png) [@fungi](https://discuss.python.org/u/fungi)\
**Post date:** [November 19, 2021, 9:26pm UTC](https://discuss.python.org/t/require-mfa-on-pypi/12077/14 "2021-11-19T21:26:38Z")

</div>

In the case of projects I work on, where everything is driven from  
code review and continuous integration, we’d likely implement a job  
which confirmed the token was still valid immediately prior to  
attempting to use it, in order to block release requests from  
merging and triggering attempts to upload new artifacts to PyPI  
until $HUMAN does the thing with its fingers to 2FA generate a new  
token and replace the old one in job definitions. Is there a  
mechanism to test a token or otherwise check its validity?

---

<div class="post-metadata">

**Author:** ![fungi](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/fungi/32/5963_2.png) [@fungi](https://discuss.python.org/u/fungi)\
**Post date:** [November 19, 2021, 9:34pm UTC](https://discuss.python.org/t/require-mfa-on-pypi/12077/15 "2021-11-19T21:34:24Z")

</div>

[…]

> Instead of time-scoping I suggest an optional two-step release  
> process. In the first step automation uses a project-scoped token  
> to create a new staging release and upload artifacts. In the  
> second a human needs to log-in with MFA and press the release  
> button. The workflow allows users to automate all the annoying  
> bits and still require MFA for the actual release.

That sounds like a great option, but definitely not something I’d  
want to see required for every project on PyPI. I work in a  
community which has volunteer release managers approving dozens (and  
sometimes hundreds) of release requests a week on demand. Requiring  
them to all have access to PyPI accounts and click buttons in a  
browser would be a step backwards.

---

<div class="post-metadata">

**Author:** ![tiran](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/tiran/32/48_2.png) [@tiran](https://discuss.python.org/u/tiran)\
**Post date:** [November 19, 2021, 9:40pm UTC](https://discuss.python.org/t/require-mfa-on-pypi/12077/16 "2021-11-19T21:40:08Z")

</div>

> [@fungi](#):
>
> That sounds like a great option, but definitely not something I’d  
> want to see required for every project on PyPI. I work in a  
> community which has volunteer release managers approving dozens (and  
> sometimes hundreds) of release requests a week on demand. Requiring  
> them to all have access to PyPI accounts and click buttons in a  
> browser would be a step backwards.

I’m with you and just put the **optional** in “optional two-stage process” in bold letters. 🙂

---

<div class="post-metadata">

**Author:** ![brettcannon](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/brettcannon/32/34895_2.png) [@brettcannon](https://discuss.python.org/u/brettcannon)\
**Post date:** [November 19, 2021, 10:29pm UTC](https://discuss.python.org/t/require-mfa-on-pypi/12077/17 "2021-11-19T22:29:18Z")

</div>

> [@stoneleaf](#):
>
> The workflow may be automated, but surely kicking it off is not?

It’s definitely within the realm of possibility.

If your package is wrapping something else off and you automate releases based on that upstream project doing releases, you could the whole process be automated and not have to touch a thing.

You could do a release automatically after every merged PR.

There’s plenty of scenarios where releases do not necessarily require a manual trigger where you’re going to want to go to PyPI and generate a new token every time.

---

<div class="post-metadata">

**Author:** ![EpicWink](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/epicwink/32/17968_2.png) [@EpicWink](https://discuss.python.org/u/EpicWink)\
**Post date:** [November 20, 2021, 8:09am UTC](https://discuss.python.org/t/require-mfa-on-pypi/12077/18 "2021-11-20T08:09:42Z")

</div>

Out of scope for this discussion, but a better solution would be an app authentication, like an OAuth flow. Long term automation shouldn’t really be authenticated with static passwords/tokens

---

<div class="post-metadata">

**Author:** ![hugovk](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/hugovk/32/14505_2.png) [@hugovk](https://discuss.python.org/u/hugovk)\
**Post date:** [November 20, 2021, 8:28am UTC](https://discuss.python.org/t/require-mfa-on-pypi/12077/19 "2021-11-20T08:28:47Z")

</div>

> [@brettcannon](#):
>
> You could do a release automatically after every merged PR.

This is exactly what Hypothesis does:

> This is all thanks to our continuous release process. We’ve completely automated the process of releasing, so every pull request that changes code gets a new release, without any human input.

> **[Hypothesis](https://hypothesis.works/articles/continuous-releases/)**

---

<div class="post-metadata">

**Author:** ![Zac-HD](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/zac-hd/32/607_2.png) [@Zac-HD](https://discuss.python.org/u/Zac-HD)\
**Post date:** [November 20, 2021, 1:16pm UTC](https://discuss.python.org/t/require-mfa-on-pypi/12077/20 "2021-11-20T13:16:00Z")

</div>

😄 Hypothesis also has a weekly automated PR, and “auto-merge if CI passes” is an obvious extension. I’d highly recommend both auto-releases and auto-weekly-maintainence to anyone, they’re lovely workflows.

Personally I’d like to keep using long-lived and non-interactive tokens, but would be happy to adopt other restrictions like “only allowed to publish new package versions with a later version number” to exclude attacks which add new wheels, or `.post1` versions, or `1.9999` to get anyone who has pinned to `pkg < 2`.

* * *

More broadly, it sounds like we have a couple of good ideas that are largely blocked on (funding for) implementation in Warehouse. In particular, I don’t see much point discussing whether we should require MFA when projects can’t yet opt-in to enforcement!

I do strongly support blocking uploads from passwords or user-scope tokens, _especially_ but not only for users with MFA enabled. Valuable even as an opt-in.

@pradyunsg - can I suggest adding MFA-and-token-related enhancements to the [fundables page](https://github.com/psf/fundable-packaging-improvements/blob/master/FUNDABLES.md)? They do seem to meet the criteria, and e.g. the [OpenSSF](https://github.com/ossf) might be interested.

[Next page](https://discuss.python.org/t/require-mfa-on-pypi/12077.md?page=2)
