Tell me your secrets: Serverless Secrets in AWS Lambda

Published:

May 07, 2021

Reading time:

10 Minutes

Introduction

As serverless technology usage grows more and more, the ability to deploy your code to the cloud and let the cloud provider do the rest makes things much easier and scalable. A research study by Orca Security into AWS Lambda (AWS’s Serverless solution service), and the secrets it uses, reveals that almost 30% of Lambda functions contain secrets in their environment variables. These secrets can be keys, authorization tokens, passwords and everything that should be kept privately – a fact that should concern organizations that use Serverless. These organizations, according to our research, each use an average of 353 serverless functions.

Don’t forget that according to the Cloud Service Provider (CSP) shared responsibility model, you are responsible for your code security and configuration security. For example, you cannot blame your cloud provider for making your function public, which would mean every authenticated user can invoke it. But you can trust your cloud provider to secure the host that the function instance executes on. To preserve the secrets you have in Lambda – or to avoid leaving them where they can be exploited – read on.

Environment Variables

Environment variables for Serverless functions are used for the same purpose as environment variables on your system: as a way to pass parameters to a more generic function. In code they are used the same way ‘regular’ environment variables are used. Their usage is very common – more than 60% of functions use them. This represents risk to organizations, as mentioned in the first paragraph: according to our research, 26.7% of functions contain secrets in their environment variables. Again, secrets can be keys, authorization tokens, passwords and everything that should be kept privately.

Two examples of AWS Lambda secrets stored in environment variables.

Figure 1: Two examples of secrets stored in environment variables.

Think of a scenario where an attacker establishes a foothold on a machine with a role that can list functions on your account. With this ability, he may be able to access more sensitive resources on your account! If someone can list your functions, they can also view your environment variables. Is that what you intended? Perhaps not.

Prevalence of AWS Lambda functions with secrets and secret type counts

Some data from our research study on the prevalence of secrets in Lambda functions. 26.3% of the functions we looked at had secrets in their environment variables.

AWS Lambda Secrets File Count Chart

Functions with Secrets Chart

Recommendations

More often than not, environment variables are employed because they are simple to use. But some alternatives to using environment variables are also fairly easy to use. It’s a good idea to compare the cost of selecting alternate solutions and choose those that meet your needs.

  1. Store the secrets in a dedicated service
    The idea behind the following solution is to use the function’s role (e.g., IAM execution role on AWS) to authorize the function to access a specific secret.

    • You can store the secrets on Secrets Manager for AWS or its equivalents on other cloud providers. This solution is great because the role of the function will only be allowed to access a specific secret. The secrets can also be rotated automatically using this service. It also has a resource-based policy, meaning the role must be specified on the secret policy itself.
    • An alternative to Secrets Manager is the Parameter Store of AWS’s System Manager service. It doesn’t have the capability to rotate secrets, but it’s less expensive.
    • Use products like HashiCorp’s Vault with IAM authorization configuration.
  2. Use different methods for access control
    Our research shows that more than 30%  of the environment variables we looked at were used with Relational Database Service (RDS) databases. This approach is not only risky, there are simpler methods. If you are using secrets to authorize a function’s access to RDS, for example, it’s much easier and safer to simply add the rds:connect permission to the function’s role policy.
  3. Encrypt your variables
    By default, environment variables are encrypted using a default custom master key (CMK ‘aws/lambda’). You can choose to encrypt variables with a dedicated key. The disadvantage of this solution is that it doesn’t truly follow the principle of least privilege and it’s expensive to manage lots of keys, so you’ll probably only manage a few, and in an organization with a lot of functions, this method doesn’t work very well.
  4. Store your secret on a bucket or equivalent
    This is a common and easy solution, but note that as a developer, it is very common to be lazy, and grant the function with the general s3:read permission, which is over-permissive. It is much harder to maintain and keep this approach really secure, and that’s why we do not recommend it.

How Orca helps

Environment variables are a useful and helpful feature, but they are often misused as a location for secret storage. There are alternative ways to store your secrets, and even alternative methods to control access to your resources.

In future articles, we will look at other Cloud Service Providers like GCP and Azure, but this type of configuration error (storing sensitive data in configuration variables) is a common security issue across all cloud platforms. It is not an issue with any specific cloud platform, but rather shows how easy it is to sacrifice security – and your secrets – depending on how you use  environment variables.

Should you take the path of using serverless functions, rest assured that Orca detects secrets in environment variables and alerts when a secret is found in your cloud estate. To learn more about Orca and its ability to see secrets hidden in your environment variables, view our demo.

 

 

AWS Lambda FAQ

What is AWS Lambda?

AWS Lambda is an event-driven, serverless computing platform that lets you run code without provisioning, configuring, or managing infrastructure. As a developer, you don’t have to benchmark your application, spin VMs or containers, perform OS maintenance, or any other administrative tasks. All you have to do is execute your code inside Lambda functions.

A Lambda function is a programmable script that runs user code in response to an event. Events are typically generated by AWS services. The Lambda runtime transforms an event into JSON-formatted data before passing it to the relevant function. The function executes the code that processes the event and returns a response.

For example, you can create a Lambda function that executes business logic upon receiving an event from the Amazon Simple Notification Service (SNS). Or you can use multiple Lambda functions to build a backend for your web application.

Lambda execution is driven by demand. Functions automatically scale up and down as required, from a few requests per hour to thousands per second. The only caveat is that functions must be stateless, so that Lambda can instantaneously launch as many copies of the function as required to efficiently process incoming requests.

Lambda offers native support for Python, Node.js, Go, Ruby, Java, and .NET. It also offers a runtime API that can be used to write Lambda functions in any other programming language. 

What is AWS Lambda for, and how does it work?

AWS Lambda is a serverless computing platform that executes user code in response to preconfigured events. Lambda users don’t worry about provisioning, managing, or scaling servers to run their applications. All they have to do is organize their code into Lambda functions.

AWS Lambda can automatically scale functions up or down as required. This makes Lambda perfect for applications that receive varying amounts of traffic during a day. Since there’s no infrastructure to manage, developers can focus on making  code more performant.

You can use Lambda to build different kinds of applications. For example, if you’re building an analytics application, you can create a Lambda function that processes data as soon as it enters the system. Or if you’re building a mobile application, you can use Lambda functions to build the backend layer for it.

When you create a Lambda function, you specify a trigger for it. A trigger is a re or configuration that can invoke a Lambda function in response to certain events. It can either be an AWS service or an event  mapping. For example, you can set Amazon Simple Storage Service (S3) to trigger your function whenever an S3 object is added or updated.

What are AWS Lambda functions?

Lambda functions are self-contained scripts that permit users to execute code on the AWS Lambda platform. A function includes code that processes configured events and returns a response. 

Users can either author Lambda functions using any of the supported languages or implement a custom runtime to use a different language. There are multiple ways to invoke a Lambda function: through the Lambda console, Lambda API, AWS CLI, an AWS SDK, an AWS toolkit, a function URL, or other AWS services.

The AWS Lambda developer guide includes all the information you need to integrate a function with other AWS services, like Amazon API Gateway, AWS CodeCommit, Amazon Alexa, and Amazon EventBridge

Once integrated, the service will trigger execution of the function in response to preconfigured events. For example, you can configure the Amazon EventBridge to run your function on a schedule. 

If you’re using Node.js or Python, you can write, edit, and test your Lambda functions directly from the AWS Lambda console using the code editor. You can also package your  code to a .ZIP file and upload it to the AWS console. 

How do I create, use, and test Lambda functions?

If you are using Node.js or Python, you can use the code editor in the AWS Lambda console to create Lambda functions. The code editor offers an intuitive, IDE-like environment to directly edit and test your functions.

If you prefer another editor or are using a language other than Node.js/Python, you can compress your  code into an archive file and upload that to the console. The .ZIP file can’t be larger than 50 MB. Java developers can use the AWS Toolkit for Eclipse to write, test, and deploy Lambda code. C# and Node.js developers can install the AWS Toolkit for Visual Studio

To use a Lambda function, you have to add triggers to it. A trigger can either be an AWS service that can be configured to invoke a function or an event  mapping. For example, you can set up the Amazon Simple Cloud Storage (S3) service to trigger a Lambda function whenever someone deletes an S3 object in a particular bucket.

There are several other ways to invoke a Lambda function: through the Lambda console, Lambda API, AWS CLI, an AWS SDK, an AWS toolkit, or a function URL.

To test a Lambda function, you can invoke it via a test event directly from the console. You can also use the Lambda Runtime Interface Emulator to test your functions in a local environment. The emulator converts HTTP requests to JSON-formatted events that are used to trigger Lambda functions running inside a container.