Stop Hardcoding Your Secrets … How To Securely Keep A Secret

I have lost count the number of code reviews that I have done where the secrets within the code are actually hardcoded into the code. The…

Stop Hardcoding Your Secrets … How To Securely Keep A Secret

I have lost count of the number of code reviews that I have done where the secrets within the code are actually hardcoded into the application. This often relates to a database password which reveals data or the secret used to sign an OAuth token for access and rights.

The first problem with this is that the application needs to be updated whenever the database or token signing password is updated (and where the password is often rarely changed as it can cause a disruption in the application deployment), and that anyone who gets access to the code can reveal the secret (and thus hack the database or the signed tokens). For best practice, passwords should always be rotated on a regular basis, and where there is no real disruption to the usage of associated applications.

So how do we create applications, and create code which can reveal it when required, but still keep the secret, secret? Well, to keep it external to an application, we could use some form of HSM (Hardware Security Module). Unfortunately, this requires physical access, and physical servers. These days, though, we can run a secrets manager in the Cloud, and lock-down access to the revealing of the secret.

So, in this article, we will see how we can store a secret in the Cloud and then reveal it. A typical application of this is to reveal a password for a database. We can then can add code to query the secret password for a database in the cloud. Also, if we change the password of the database, we would have to update the code in an application, whereas using cloud storage of the secret will allow us to easily update the code.

Keeping a secret

As humans, we have secrets. In cybersecurity, we have secrets. In our lives, we have secrets. But where should we keep them? Well, AWS Secrets Manager allows you to store your secrets, and then use them for database credentials, API keys and OAuth tokens. If we want to change our secrets over time, we can implement secret rotation. Once created, we can then expose our secrets when they are needed.

In Figure 1, we see that Alice has updated the password on the database (1). She then stores the secret password in the AWS Secrets Manager (2). Next, when Bob’s application wants to use the database, it retrieves the password from the Secrets Manager (3), and then applies this to access the database (4).

Figure 1: Updating with AWS Secrets Manager

In AWS, we can use the CLI (Command Line Interface) to create the secret. In this case, we will create a secret name of my-secret-passphrase, and with a secret value of “Qwerty123”:

$ aws secretsmanager create-secret --name my-secret-passphrase \
--secret-string Qwerty123
{
"ARN": "arn:aws:secretsmanager:us-east-1:960372818084:secret:my-secret-passphrase-PXQxIK",
"Name": "my-secret-passphrase",
"VersionId": "2d37562d-6d5c-43bf-8e6f-d000d1010ad6"
}

We now see our secret from the AWS console:

The cost of keeping a secret is $0.40 per secret per month and $0.05 per 10,000 API calls. We can list our secrets with:

$ aws secretsmanager list-secrets
{
"SecretList": [
{
"ARN": "arn:aws:secretsmanager:us-east-1:960372818084:secret:my-secret-passphrase-PXQxIK",
"Name": "my-secret-passphrase",
"LastChangedDate": "2022-11-28T11:57:44.314000-08:00",
"LastAccessedDate": "2022-11-27T16:00:00-08:00",
"SecretVersionsToStages": {
"2d37562d-6d5c-43bf-8e6f-d000d1010ad6": [
"AWSCURRENT"
]
},
"CreatedDate": "2022-11-28T11:57:44.257000-08:00"
}
]
}

And then, we can reveal the value of the secret with:

$ aws secretsmanager get-secret-value --secret-id my-secret-passphrase
{
"ARN": "arn:aws:secretsmanager:us-east-1:960372818084:secret:my-secret-passphrase-PXQxIK",
"Name": "my-secret-passphrase",
"VersionId": "2d37562d-6d5c-43bf-8e6f-d000d1010ad6",
"SecretString": "Qwerty123",
"VersionStages": [
"AWSCURRENT"
],
"CreatedDate": "2022-11-28T11:57:44.309000-08:00"
}

and to display just the secret we perform a query:

$ aws secretsmanager get-secret-value --secret-id my-secret-passphrase \
--query SecretString --output text
Qwerty123

We can change the secret with:

~$ aws secretsmanager update-secret --secret-id my-secret-passphrase \
--secret-string Password123
{
"ARN": "arn:aws:secretsmanager:us-east-1:960372818084:secret:my-secret-passphrase-PXQxIK",
"Name": "my-secret-passphrase",
"VersionId": "ad54e233-c4ed-46f0-9c4d-7d06ba5d16c1"
}

And then show that it has changed:

$ aws secretsmanager get-secret-value --secret-id my-secret-passphrase \
--query SecretString --output text
Password123

We can list all the previous secrets with:

$ aws secretsmanager list-secret-version-ids --secret-id my-secret-passphrase
{
"Versions": [
{
"VersionId": "2d37562d-6d5c-43bf-8e6f-d000d1010ad6",
"VersionStages": [
"AWSPREVIOUS"
],
"LastAccessedDate": "2022-11-27T16:00:00-08:00",
"CreatedDate": "2022-11-28T11:57:44.309000-08:00"
},
{
"VersionId": "ad54e233-c4ed-46f0-9c4d-7d06ba5d16c1",
"VersionStages": [
"AWSCURRENT"
],
"LastAccessedDate": "2022-11-27T16:00:00-08:00",
"CreatedDate": "2022-11-28T12:05:56.554000-08:00"
}
],
"ARN": "arn:aws:secretsmanager:us-east-1:960372818084:secret:my-secret-passphrase-PXQxIK",
"Name": "my-secret-passphrase"
}

And then delete a secret:

$ aws secretsmanager delete-secret --secret-id my-secret-passphrase
{
"ARN": "arn:aws:secretsmanager:us-east-1:960372818084:secret:my-secret-passphrase-PXQxIK",
"Name": "my-secret-passphrase",
"DeletionDate": "2022-12-28T12:14:25.224000-08:00"
}

This will delete the secret in 30 days' time (28 Dec 2022, and the current date is 28 Nov 2022). Normally, the time to delete the secret is between 7 and 30 days.

If we have deleted a secret by mistake, we can show it in the Preferences for secrets, and display “Delete on”:

and then, we can see the deleted secrets:

and then “Cancel deletion”:

If we wish to view the secret in Python we can install:


$ pip install aws-secretsmanager-caching

and then run the Python code of:

import botocore 
import botocore.session
from aws_secretsmanager_caching import SecretCache, SecretCacheConfig

client = botocore.session.get_session().create_client('secretsmanager')
cache_config = SecretCacheConfig()
cache = SecretCache( config = cache_config, client = client)

secret = cache.get_secret_string('my-secret-passphrase')
print ("Secret: ",secret)

This gives:

Secret:  Password123

Using envelope encryption

With Secrets Manager, whenever a new secret is created, the KMS (Key Management Service) creates a new encryption to encrypt the data key, and which is then used to encrypt the protected secret data. This is known as envelope encryption. When the secret is accessed, the KMS key is used to decrypt the data key, which then decrypts the secret data. The data key is never stored in an unencrypted form, and is always disposed of after it has been used. Another layer of protection that is applied is the mandatory use of a TLS connection for the request for secrets.

Conclusion

The AWS Secrets Manager is an excellent way to have a highly secret place for all your secrets that you need for your applications, such as for databases and tokens. When required, we update them and have a place in our applications that will go and seek them. If we find our secrets have been discovered we can just change them. Along with this, we can rotate them, in order to improve our security.

https://billatnapier.medium.com/membership