jaraco / keyring

MIT License
1.26k stars 159 forks source link

Multiple Password Popups on macOS for Multiple Credentials When the Python Binary Updates #619

Open ChristopherHammond13 opened 1 year ago

ChristopherHammond13 commented 1 year ago

Describe the bug Firstly, I believe this may be related to #519?

When the Python binary updates (or a new virtual environment is created), I will receive a popup asking for my login/keychain password as usual. However, in my application (https://github.com/crowdstrike/Falcon-Toolkit) it is possible to configure many 'profiles', each of which will hold a separate secret (in the case of that application, it's an API Client Secret). Strangely, I will get the popup repeatedly, one per profile, so eight configurations with eight separate Client ID / Client Secret pairs will result in me needing to type my password eight times even if I click "Allow All". Given that I work at a security company, and my password is therefore pretty complex, typing my password n times every time I update Python or the virtual environment's Python binary is far from convenient :) I expect this to affect our MSSP and internal users a fair bit, too, if they use this tool across multiple client environments with separate sets of credentials.

I do not known if this is a bug in the way I am using keyring (I am always open to it being my fault!), keyring itself, or the macOS implementation of the binary validity checks. An example of the code I have that retrieves this value is here, in case this helps: https://github.com/CrowdStrike/Falcon-Toolkit/blob/main/falcon_toolkit/common/auth_backends/public_single_cid.py#L44. The code will run once per profile. I am using the Client ID as the username and the Client Secret as the password, with the service name defined as a constant in the code.

To Reproduce

  1. Create an application that stores multiple named secrets to the macOS secrets store
  2. Upgrade Python so that the binary changes
  3. Attempt to retrieve / load all those secrets in one go
  4. Observe one popup requesting the password for each loaded secret

I expect I can re-architect some of this code to defer the password request to later in the execution, but I still feel like there is a bug here as loading multiple credentials early in the execution is (in my opinion) quite a reasonable thing to do.

Expected behavior I should be asked for my password once, and the "Allow All" button should stop future popups until I change the Python binary again.

Environment

$ pip list | grep keyring
...

keyring 23.13.1

$ keyring --list-backends
...
keyring.backends.fail.Keyring (priority: 0)
keyring.backends.macOS.Keyring (priority: 5)
keyring.backends.chainer.ChainerBackend (priority: -1)

Additional context Again, I am very open to this being a code issue on my part if I am doing something stupid here and/or invoking keyring in the wrong way. Apologies in advance if this is the case, and thank you for any tips you can provide!

jaraco commented 1 year ago

I use macOS and keyring quite a bit and I have a very similar experience. Every time the binary changes, it has to be authorized with each item in the keychain. In my experience, "Allow" means allow for this instance but prompt me for the keychain password again next time and "Allow All" means allow for this executable and item. I'm not aware of a way to authorize an executable for all items or to allow all items to work with a future executable.

I don't think you're doing anything wrong, but rather this interface is simply the security landscape provided by macOS. The keyring library has very little control over the user experience that macOS provides.

It's conceivable that Apple provides some more advanced API features that would enable a more streamlined experience, but my guess is not - that they've designed this behavior for security and wouldn't want an application to be able to compromise those choices.

Feel free to take a look at keyring.backends.macOS.api to see the implementation that interfaces with macOS and explore Apple's options for customizing the behavior.

ChristopherHammond13 commented 1 year ago

Hey @jaraco, thank you so much for taking the time to look at this! Whilst I'm glad I haven't done anything explicitly wrong here, it sucks that Apple has chosen to make this user experience objectively awful :( Right now, Keyring / the macOS Keychain is by far the best way to implement secure secrets storage in Python from what I can see. I am considering lazy loading the secrets at configuration parse time and then truly loading them on tool execution. It's not super ideal, but it means users not having to type their passwords in once per profile on running the tool.