Securing Secrets in AWS Lambda

Image for post
Image for post
Photo by Ben White on Unsplash

AWS Lambda functions can have the need to store secrets/sensitive information. For e.g.: credentials to talk to database, api keys or other such secrets. This article outlines the various options to securely work with credentials in AWS Lambda.

Storing Secrets in Environment Variables

Environment variables allow one to store configuration data outside of the function code. This allows to change the configuration data across different environments without the need to modify the code. Secrets can be stored/provided via environment variables since they are encrypted at rest by AWS using AWS KMS. When they are accessed in the function code, they are decrypted by AWS.

In AWS Console, expand Encryption configuration section in AWS Lambda

Image for post
Image for post
Encryption configuration in AWS Lambda

As can be seen above, there is aa default key already selected which will be managed by AWS using AWS KMS.

In AWS Console, go to AWS KMS and check that a default key has been created for AWS Lambda under AWS Managed Keys

Image for post
Image for post
Default Encryption key for AWS Lambda

There are 2 points to be aware of :

  • Environment variables are not encrypted in transit i.e. during deployment.

Using AWS Secrets Manager

AWS Secrets Manager allows to easily manage secrets . Users and applications retrieve secrets with a call to Secrets Manager APIs, eliminating the need to hardcode sensitive information in plain text. We can use the service to store secrets used by AWS Lambda functions and retrieve them in a secured manner. Lets see how to use it with our function.

Instead of storing the API Key in environment variable, we will store it in AWS Secrets Manager. We will then use Secrets Manager SDK in our function code to retrieve the API key.

  • Create an IAM user/role with secretsmanager:CreateSecret permissions.
aws iam put-user-policy --user-name <username> --policy-name create-secrets --policy-document file://create-secrets.jsoncreate-secrets.json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "secretsmanager:CreateSecret",
"Resource": "*"
}
]
}
  • We will use AWS CLI to create a secret. Make sure you have AWS CLI configured for the user with appropriate permissions.
aws secretsmanager create-secret --name ApiKey --secret-string 23do9jer
  • Add ApiKey as an environment variable so that we can read the value in our function code. This is just the name of the secret and not the secret value
// Load the AWS SDK
var AWS = require('aws-sdk');
// Create a Secrets Manager client
var client = new AWS.SecretsManager({
region: process.env.REGION
});
// function to retreive api key from secrets manager
async function getAPIKey() {
return new Promise((resolve,reject)=>{
client.getSecretValue({SecretId: process.env.APIKEY_NAME}, function(err,data){
if(err) {
// handle all exceptions and take appropriate actions
reject(err);
} else {
resolve(data.SecretString);
}
}) ;
});
}
exports.handler = async (event) => {
const apiKey = await getAPIKey();
// use the api key to talk to a service
return response;
};

AWS Secrets Manager has lots of capabilities around secrets management. For e.g.: to connect to RDS databases and rotate credentials regularly. This means that credentials for the database are secure, rotated regularly, and available anytime your lambda code needs them.

One point to be aware of when using this approach is that it adds latency to the lambda execution, but it greatly improves security.

EC2 Systems Manager Parameter Store

Instead of storing the API Key in environment variable, we will store it in EC2 Systems Manager Parameter Store. We will then use SSM SDK in our function code to retrieve the API key.

  • Create an IAM user/role with ssm:PutParameter permissions.
aws iam put-user-policy --user-name furqanadmin --policy-name ssm-putparameter --policy-document file://ssm-putparameter.jsonssm-putparameter.json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "ssm:PutParameter",
"Resource": "*"
}
]
}
  • We will use AWS CLI to create a secret. Make sure you have AWS CLI configured for the user with appropriate permissions.
aws ssm put-parameter --name "APIKey" --type "String" --value "dd23d5frg"
  • Add ApiKey as an environment variable so that we can read the value in our function code. This is just the name of the secret and not the secret value.
// Load the AWS SDK
var AWS = require('aws-sdk');
// Create a SSM client
var ssmClient = new AWS.SecretsManager({
region: process.env.REGION
});
// function to retreive api key from SSM Paarmeter Store
async function getAPIKey() {
return new Promise((resolve,reject)=>{
ssmClient.getSecretValue({Name: process.env.APIKEY_NAME}, function(err,data){
if(err) {
// handle all exceptions and take appropriate actions
reject(err);
} else {
resolve(data.Parameter.Value);
}
}) ;
});
}
exports.handler = async (event) => {
const apiKey = await getAPIKey();
// use the api key to talk to a service
return response;
};

One point to be aware of when using this approach is that it adds latency to the lambda execution, but it improves security.

Securely storing credentials will ensure that your serverless applications are secured.

References

Written by

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store