When you are developing applications it is essential to properly manage secrets of all kinds. When building an application you might run into several different kinds of secrets. Here are two main categories that we’ll look at and learn how to properly manage within this guide:
- Account passwords (e.g. Username/Password logins for systems like PipeDrive, LinkedIn etc.)
- Infrastructure passwords, API keys, and other secrets (e.g. database passwords and connection strings).
When you’re working with account passwords that you use to sign into different accounts you should be considering a few things:
- Does a user need an account? Not all users need accounts for every service! If you don’t need to provision new account access then follow the Principle of Least Privilege and just don’t create a new account!
- Can you use a Single Sign-On (SSO) option that is tied to an existing central identity store such as Azure AD and Microsoft accounts or Google App Suite for Business? If you can provisioning new accounts for employees using SSO, that is the lowest friction and easiest way to manage new services because there are fewer logins required for different tools and you only maintain one centralized login
- If a user does require a new account and that account cannot be connected purely through SSO then when possible it should be created/updated through an automated provisioning process of some kind that ties into a central identity infrastructure such as Azure AD.
But, often people just need new accounts to do their work and you can’t always take the previous steps!
When this happens, an organization should provide employees with access to a password manager to store longer, unique, randomly generated, passwords for each account. Some password managers can be themselves configured with SSO through something like Azure AD. But if a password is required to access yours, then it should be memorable and secure.
To meet both of these criteria, consider using a 6 or 7-word Diceware password.
Infrastructure Passwords, API Keys, and Other Secrets
While in some cases infrastructure passwords may make sense to include in a password manager, they are very sensitive credentials that should be handled carefully and will more commonly need to be accessed by your applications when they run. There are a few methods of doing this depending on the scenario and the sensitivity of the secrets.
Storing Secrets in CI/CD and Injecting them during Deployment
One common method for handling secrets is to store them in a CI/CD system like Bitbucket Pipelines, Azure DevOps, TravisCI, and others. When doing this, the build system can be used to include secrets within your deployed applications, usually as environment variables.
Here are a few example guides on how this can happen within different systems:
When using a system like this, it’s important to make sure that:
- The access to those secrets within the system is limited to as few people as possible
- The system doesn’t inadvertently expose secrets through build logging or other means
- That if the system is integrated with any public-facing build pipelines (think GitHub and open source projects) that those build pipelines do not allow exposure or abuse of any sensitive secrets, especially through things like automated builds for PRs.
These sorts of systems are great when you need to inject secrets at build time. This can often be because an application or operating system requires embedded keys when it is built in order to function.
Storing Secrets in a Cloud Provider and Accessing them at Runtime
In addition to embedding secrets within an application during deployment are also other options for internet-connected applications that are deployed in the cloud.
When you deploy an application inside of the cloud, it can usually be given permissions using the permissioning tools within that cloud. For example:
- AWS EC2 Instances, Lambda Functions, ECS clusters, and other AWS services can be given AWS IAM permissions
- Both Azure and GCP have similar tools for managing permissions
If you deploy an application into the cloud with the permissions to access particular secrets inside of a secrets store, then your application can make a call to fetch those secrets when it starts up, or every time it needs to fetch those secrets depending on which is more appropriate.
This means, that if your application code is exposed or even potentially your deployment package, an attacker will not have the permissions required to get the secrets from the cloud provider in the first place.
As an example, say you have an AWS application that needs a MySQL Database Password to run properly. Let’s say the name of the secret is MYSQL_PASSWORD and its value is
- First, we would create a new „Secret“ or „Parameter“ in a service like AWS Parameter Store or the Secrets Manager called MYSQL_PASSWORD and give it the value of
- Then, we would deploy our application to AWS and give it IAM permissions to access the AWS Parameter Store service
- The application would then have the ability to fetch the MYSQL_PASSWORD value
Now, we would still need to secure the process of granting IAM permissions and make sure only a few people or systems have access to do this. But this scenario means our AWS application now has the IAM permissions it needs to fetch the MYSQL_PASSWORD at runtime using an AWS SDK and the relevant method. With AWS SDK for Python, boto3, that would be the get_parameter operation or the equivalent AWS Secrets Manager operation to get the MYSQL_PASSWORD value.
Here’s an example of how we’d do this in a Python application: