Stop saving credential tokens in text files
You’ve probably experienced this behavior if you’ve ever used a Command Line Interface (CLI) for a SaaS service:
- You download the CLI.
- It asks you to login or generate an access token.
- It stores those credentials in a hidden file in your file system.
This might sound reasonable if, as the user, you encrypt your file system. You can guarantee that nobody has access to it when it’s not encrypted. However, the only point in time when the file system is guaranteed to be encrypted is when the computer is completely powered down. There are different techniques to recover encryption keys from memory when your computer is asleep, but I won’t go into those details in this post.
You cannot take any of those assumptions for granted if you’re developing a CLI. The only responsible way to store your user credentials is to store them is in a secure vault.
Modern Operating Systems provide tools and APIs to store secrets in a secure way. Apple’s secret manager is called Keychain, Windows has a Credentials Manager API, and Linux offers a standard API called Secret Service.
These tools are very well defined, offer strong guarantees to secure credentials, and are widely adopted by consumer software. Your Operating System uses them to store your WiFi passwords, your browser uses them to store credentials to access your Twitter account, and so on.
When we talk about CLI applications, developers seem to dismiss these tools completely, although there are exceptions. Git has the concept of credential stores and helpers, and I added a similar concept (very inspired by Git’s implementation) to the Docker Engine in the 1.11 version.
My intention when I wrote Docker’s implementation was to design a very simple proxy to the Operating Systems’ native stores. I did this with the goal of helping other developers adopt good practices for storing encrypted access tokens.
If you use The Go Programming Language to write CLI applications, accessing a native credential store is very easy with Docker credential helpers. This is the TL;DR for Linux’s Secret Service:
Using other native stores is as easy as replacing the implementation of that `nativeStore` variable; the rest of the code remains the same. Swap that with `osxkeychain.Osxkeychain` and `wincred.Wincred` and you’ll be all set to target different operating systems.
Actually, you don’t even need that logic in your application. Very inspired by Git, again, Docker credential helpers provide encapsulated static binaries that you can use as sidekicks for your CLI to handle all the credential management. They use a more simple protocol than Git’s helpers, and the messages are plain JSON objects. The only requirement is to have the correct binary in your system. Then, you can send and receive information between your application and that binary using standard inputs and outputs.
I wrote a fake credential store for Docker’s integration tests that can help you understand the protocol:
You can use a simple shell script with a JSON document to store the credentials:
And you can use an even simpler script to get credentials back from the store:
You can replicate that behavior with any programming language you prefer. If you use Go, grab the client that I wrote for the Docker Engine itself. Integrating it into your application is as simple as copying the code below:
From my own experience, I know that native APIs can look quite daunting. They could probably be more widely adopted with better integration examples. I hope this blog post helped you understand why it’s important to keep your user credentials safe and how easy that can be when we put simpler abstractions on top of those APIs.