Encrypt Secrets with keyring
Learn to encrypt secrets with the keyring library in this Secure development tutorial — hands-on steps, troubleshooting, and what to study next.
Focus: encrypt secrets with keyring library
Picture this: you've just spent hours hardening your application, reviewing every input validation rule, and locking down your API's authentication flow. Then you need to store a database password, an API key, or a service account credential so your script can use it — and your first instinct is to drop it into a config file or environment variable. That instinct is a security debt you're leaving for yourself. Hardcoded secrets in source code, config files, or even plaintext environment variables are one of the most common ways credentials leak in production. This lesson introduces the keyring library — a cross-platform, operating-system-backed secret store — and shows you how to encrypt secrets with the keyring library to keep them out of your codebase entirely.
By the end, you'll be able to store and retrieve secrets securely on Windows, macOS, and Linux, and you'll know exactly how to handle edge cases like missing backends or CI environments. Let's start by understanding the problem this lesson solves.
The problem this lesson solves
Storing secrets securely is harder than it looks. Here's the reality:
- Hardcoded strings in Python files end up in version control history forever, even if you delete them later.
- Plaintext environment variables are visible to any process that can read
/proc/<pid>/environor the Windows registry. - Config files (like
.envor YAML) are often committed by accident or left world-readable on shared servers.
You've probably seen the aftermath: a leaked AWS key in a public repo, a Stripe secret in a pastebin, or a database password in a test suite's output. The cost is severe — compromised infrastructure, stolen data, and hours of cleanup.
And there's a deeper issue: encryption alone isn't enough. If you encrypt a secret and store the encryption key next to it in the same directory, you've just obfuscated the problem, not solved it. The real solution is to rely on the operating system's credential manager, which protects secrets at the OS level with strong encryption, access control, and user-specific scoping.
That's exactly where keyring comes in.
Core concept / mental model
Think of keyring as a secure vault for your application's secrets — a vault that the operating system itself guards.
- On Windows, it uses the Windows Credential Locker.
- On macOS, it uses the Keychain.
- On Linux, it uses a Secret Service API (like GNOME Keyring or KWallet), or a fallback plaintext file in
~/.local/share/keyringif no keyring daemon is available.
The mental model is simple: you give a service name and a username, and the vault stores the secret. Later, you ask for the secret using the same pair. The secret never touches your code, your disk, or your version control.
The magic is that keyring doesn't make you manage encryption keys — the OS does that for you. The OS encrypts the secret at rest using your login credentials or a hardware-backed key, and only your user account can decrypt it.
How it works step by step
Here's the flow when you use keyring:
- Install the library with
pip install keyring. - Set a secret with
keyring.set_password(service_name, username, password). The library picks the right backend for your OS, encrypts the value, and stores it. - Get a secret with
keyring.get_password(service_name, username)— the library retrieves it and decrypts it in memory. - Delete a secret with
keyring.delete_password(service_name, username)when you rotate credentials or remove a service.
That's the entire core API. There's also a CLI, keyring command, so you can test the setup right from your terminal.
Hands-on walkthrough
Let's put it into practice. First, install keyring:
pip install keyring
Now, let's store and retrieve a secret — for example, a database password you don't want in your source code.
import keyring
SERVICE_NAME = "my_app"
USERNAME = "db_user"
# Store the secret — this will probably prompt for your OS login on first use
keyring.set_password(SERVICE_NAME, USERNAME, "s3cr3t-password")
# Retrieve it
secret = keyring.get_password(SERVICE_NAME, USERNAME)
print(f"Retrieved secret: {secret}")
Expected output:
Retrieved secret: s3cr3t-password
That's it. Your secret is now in the OS's secure vault, not in a .env file or a config.
Now let's handle a real-world scenario: you have an API key that your application loads at startup. Here's a complete example with a fallback for development:
import os
import keyring
SERVICE = "my_saas_app"
# Try to get the API key from the keyring; if not found, prompt the user
api_key = keyring.get_password(SERVICE, "api_key")
if not api_key:
api_key = input("Enter your API key: ")
keyring.set_password(SERVICE, "api_key", api_key)
# Now you can use the key
def make_request():
print(f"Using API key: {api_key[:4]}...")
make_request()
Expected output (first run):
Enter your API key: my-secret-key
Using API key: my-s...
And on subsequent runs, the prompt won't appear because the key is already stored.
Pro tip: You can also use the command-line interface for quick testing:
bash keyring set my_app db_user keyring get my_app db_user
Compare options / when to choose what
You might be thinking, "Why not just use environment variables or a vault service?" Great question. Here's a comparison:
| Approach | Security Level | Ease of Use | Cross-platform | Best for |
|---|---|---|---|---|
| Hardcoded strings | 🔴 Terrible | Easy | Anywhere | Nothing |
| Environment variables | 🟡 Moderate | Easy | Anywhere | Non-secret config, quick prototypes |
| Config files (encrypted or not) | 🟡 Moderate | Moderate | Anywhere | Docker secrets, but easy to commit accidentally |
keyring library |
🟢 Strong (OS-backed encryption) | Easy | Windows/macOS/Linux | Desktop apps, local scripts, user-specific secrets |
| Dedicated vault (e.g., HashiCorp Vault) | 🟢 Very strong | Complex | Cloud/anywhere | Server-side apps, centralized secret management |
When to choose keyring:
- You're building a desktop application or a local script that runs under a specific user account.
- You want the secret to be tied to the user, not the machine's environment.
- You want to avoid the complexity of setting up a cloud vault.
When not to choose it:
- In a CI/CD pipeline where there's no interactive user session (although
keyringhas akeyrings.altpackage with a file-based backend for such cases). - For web services that need to share secrets across multiple servers — a vault is better.
Variation:
keyringcan also work with third-party backends likekeyring.backends.kwallet, and you can even implement a custom backend if you need to.
Troubleshooting & edge cases
Even a simple library can bite you. Here are common issues and fixes:
1. No recommended backend was available
On Linux without a keyring daemon running (e.g., headless server), keyring might raise an error. Fix: install keyrings.alt package and use a file-based backend.
import keyring
from keyrings.alt.file import PlaintextKeyring
keyring.set_keyring(PlaintextKeyring())
Or in newer versions, you can set the environment variable PYTHON_KEYRING_BACKEND=keyring.backends.chainer.ChainerBackend.
2. Permission errors on macOS Keychain
If you see OSError: The given code cannot be used to authenticate. — make sure your script runs under your user account and you've allowed access when the prompt appears.
3. Secrets not found after reboot
On some Linux setups, the keyring daemon isn't unlocked at login, so get_password returns None until you authenticate. Use keyring.get_password and check for None, then prompt the user or store again.
4. Using in CI without a display
Set up a headless keyring with keyrings.alt or use environment variables as an alternative in CI, because there is no interactive login.
What you learned & what's next
In this lesson, you learned the core idea behind encrypt secrets with keyring library: you offload the encryption and secure storage to the operating system's credential manager. You now know how to set, get, and delete secrets programmatically, and how to select keyring over other options like environment variables or full vault solutions. You've also seen how to troubleshoot common backend issues on different platforms.
Next up in this track, you'll learn how to rotate secrets and integrate secure storage into your deployment pipelines. With keyring in your toolbox, you're one step closer to writing applications that keep secrets truly secret — not just obfuscated.
Now, try the practice recap below to cement the concept.
Practice recap
Write a small script that asks for a username and an API key, stores them with keyring, and then retrieves them on the next run without prompting. Use the keyring CLI to list and delete the stored secrets. This will reinforce the core API and help you understand OS backend behavior.
Common mistakes
- Hardcoding secrets in code and then pushing to version control — even if you remove it later, it remains in history.
- Storing the encryption key in the same file as the secret, pretending that's 'encryption' — the key must be separate and protected.
- Assuming
keyringworks in headless CI jobs — you need a backend likekeyrings.altor fallback to env vars. - Forgetting to check for
Nonewhen retrieving a secret; if the backend isn't unlocked, you getNoneand your app may crash. - Using the same keyring service name/username pair for different environments, causing secret collisions.
Variations
- Use a file-based keyring via
keyrings.altfor non-interactive environments, but understand it's less secure. - Integrate with cloud secret managers (e.g., AWS Secrets Manager, HashiCorp Vault) for serverless or multi-node apps.
- Use environment variables for non-secret configuration, but never for real credentials when
keyringis available.
Real-world use cases
- Desktop app storing a user's API token (e.g., GitHub token) so the user doesn't re-auth every run.
- Script that retrieves database credentials for a cron job, ensuring the password isn't in the script file.
- CLI tool that saves a cloud provider's access key locally, protected by the OS keychain.
Key takeaways
keyringuses OS-native credential stores to encrypt and protect secrets at rest.- Use
set_password,get_password, anddelete_passwordfor basic secret lifecycle. - Never commit secrets to source control; use environment variables only for non-sensitive config.
- For Linux headless setups, configure a file backend via
keyrings.altor handleNonegracefully. - Compare
keyringagainst full vault solutions based on whether you need centralized, multi-user secret management.
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.