With the growing adoption of cloud technologies, the use of serverless functions too is on the rise. AWS Lambda, also known as AWS serverless functions, is one of the most popular function-as-a-service offerings in the cloud computing space. As an event-driven serverless function, Lambda responds to various events such as HTTP requests and trigger applications. The service provides a runtime environment, enabling users to run their applications on the cloud without having to set up any physical infrastructure. Developers can therefore concentrate on business logic instead of managing the necessary resources for running the application.
When it comes to Lambda functions, both security and compliance are a shared responsibility between developers and AWS. While AWS secures the cloud infrastructure and underlying operating system, network, and execution environment; it is up to the developers to enforce security measures through access control mechanisms, to write secure code and libraries, and to follow security best practices as outlined by AWS.
This article provides an overview of best practices for developers to help secure their Lambda functions in the cloud. Using a practical example, we demonstrate how to define IAM policies that allow reading files from an S3 bucket. Finally, we discuss add-on tools that can enhance the security of AWS Lambda functions.
6 security best practices for AWS Lambda functions
AWS Lambda is hosted on Amazon cloud servers, which by default are secured physically by Amazon. Let’s take a look at common best practices for securing AWS Lambda functions.
1. Least-privileged IAM roles for the Lambda function
Lambda functions, just like any other AWS service, require a service role to execute. This role defines which resources and services the Lambda function can access, and is defined using the AWS Identity and Access Management console. The permissions granted to each Lambda function should be minimal and exhaustive.
For example, if a Lambda function reads a file from a bucket in S3, the IAM policy should be defined in such a way such that allows reading only from that specific S3 bucket and as opposed to the entire S3:
{
“Version”: “2012-10-17”,
“Statement”: [
{
“Sid”: “ReadFromS3”,
“Action”: [
“s3:GetObject”,
“s3:GetObjectVersion”
],
“Effect”: “Allow”,
“Resource”: [
“arn:aws:s3:::my-bucket/*”
]
}
]
}
2. Separate roles for each Lambda function
It is a best practice to create an IAM role for each Lambda function so that every Lambda function has a one-to-one relationship with its IAM role. Having a tightly coupled Lambda role helps ensure the principle of least privilege while also enabling developers to write granular policies.
3. Use API Gateway to make functions available
When serving Lambda functions to a larger audience, it is good practice to expose it via API Gateway, an AWS service that accepts HTTP GET or POST requests and triggers Lambda functions as defined.
AWS API Gateway provides protection against DDOS attacks; it also enables rate limiting and smooth integration with AWS Cognito. It is thus a service that could potentially be exposed to the public.
4. Clean up the /tmp directory
When AWS Lambda starts execution, it creates a temporary file system under the /tmp directory. This directory provides temporary storage for the function to store runtime information such as execution environment, code, and libraries. By default, each time the function completes a successful execution, the /tmp directory is cleaned up. However, if the function somehow fails to execute gracefully, the /tmp directory will not be automatically cleaned up, which could lead to the exhaustion of disk space.
To avoid such scenarios, developers should consider manually cleaning up the /tmp directory before running the function. This is easy to achieve using a utility function added to the actual Lambda function.
5. Use AWS Secrets Manager to store security credentials
While developing applications with AWS Lambda, you often need to provide runtime security credentials for activities such as connecting to a database or calling a web API. Lambda provides an interface to declare such security credentials using environment variables. While environment variables are tightly integrated with the Lambda function, one disadvantage of this approach is that the credentials are stored in plain text format, which could easily be compromised during deployment.
AWS Parameter Store, a service available in AWS Secrets Manager, allows you to store and share such credentials securely. It also offers encryption. You will need to modify the Lambda functions IAM policies to allow reading security credentials from the Parameter Store.
6. Do not log sensitive information in the console
Developers often print various statements to the standard output. This is usually done in development, as it makes debugging easier. However, you should avoid logging sensitive information in the console, which could pose a potential security breach that is often easily overlooked.
As a best practice, use Amazon CloudWatch Logs to store logs from the Lambda function. This will require additional permissions in the IAM role to allow the Lambda function to write to CloudWatch Logs. The CloudWatch Logs are created in a log group. While developers have the flexibility to choose the format, all logs from a Lambda function will be written to the same log group.
Practical Demo: reading from S3 Buckets
Next, we’ll look at how to define secure IAM policies that allow a Lambda function to read from a specific bucket in S3. The practical steps involved include:
- Creating a bucket in S3 and uploading a file
- Creating a Lambda function in your preferred programming language
- Creating an IAM role and policy that allows reading from the S3 bucket created
- Executing and validating the Lambda function
For the purpose of demonstration, we will proceed with Python as the chosen language. We will read a simple JSON file from the S3 bucket and print its contents to the logs.
1. Preparing the S3 bucket
We have created an S3 bucket, “content-data-003,” and uploaded the “content-data.json” file into it.
2. Creating the AWS Lambda function
Next, we’ll create a simple Python application that can read from the S3 bucket and print its contents to the log console. In order to work with AWS resources, we’ll use the Boto3 Python library to create an S3 client, and then read the file as an object.
import boto3
def lambda_handler(events, context):
# Create S3 client
client = boto3.client(
“s3”,
region_name = “eu-west-1”
)
# Read the file
data = client.get_object(
Bucket = “content-data-003”,
Key = “contents.json”
)
content_data = data[‘Body’].read().decode(‘utf-8’)
print(content_data)
3. Creating IAM role and policy
By default, the Lambda function will not be able to read from the S3 bucket. To do that, we need to explicitly grant the function reading rights to read from the S3 bucket. The following policy will allow the Lambda function to read a specific file from S3:
{
“Version”: “2012-10-17”,
“Statement”: [
{
“Sid”: “AllowS3Read”,
“Effect”: “Allow”,
“Action”: [
“s3:GetObject”,
“s3:GetObjectVersion”
],
“Resource”: “arn:aws:s3:::content-data-003/contents.json”
}
]
}
4. Execution and validation
Once all of the above steps have been completed, we can run and test the function. Click on “Test,” and let the function execute. If the permissions are declared correctly, then Lambda will execute the function and display the contents of the file in the Lambda console as follows:
This allows developers to define fine-grained access control policies that follow the principle of least privilege.
Add-on tools to secure AWS Lambda functions
While securing Lambda functions is one of the key responsibilities of developers, it is not always possible to execute thorough manual checks for each and every Lambda in an account. In some cases, application logic can become so complex, making it virtually impossible to detect defects and security compromises without the help of external tools.
In this section, we’ll review some of the additional tools available to enhance the security of Lambda functions while monitoring for code quality and potential security breaches.
Snyk
Snyk is an open-source, developer-first application that checks for vulnerabilities in your codebase. Offering container security and infrastructure as code (IaC), it can be installed on local computers and integrated with IDE.
Key features include:
- Find and fix issues in development: The Snyk scanner, which can be integrated with your favorite IDE (e.g., PyCharm or VS Code), allows you to run Snyk commands to check for potential issues. The scanner quickly checks the codebase and provides actionable remedies to mitigate issues.
- Integration with source control: Snyk can be installed on source control repositories like BitBucket and GitHub, allowing it to scan through all projects, libraries, and dependencies to identify security issues.
AWS X-Ray
AWS X-Ray is a distributed tracing service from Amazon that provides request tracing, exception collection, and profiling capabilities for Lambda functions. This allows you to analyze and debug distributed production workloads and potentially identify any risks that might be involved.
Monitoring and logging
Logging and monitoring active production workloads is an important part of the DevSecOps lifecycle. Once an application is deployed to production, it should be continuously monitored, with anomalies being reported immediately. By default, Amazon CloudWatch is integrated with Lambda functions, but third-party tools such as DataDog and New Relic provide extended functionalities and enhance the monitoring features to a great extent.
Conclusion
AWS Lambda allows you to focus on writing application logic without needing to worry about infrastructure. Following the shared responsibility model, AWS is responsible for the security of the cloud infrastructure, while developers are responsible for security within the cloud.
By default, Lambda functions are not secure, and it is up to the developers to define the proper strategies to enhance the security of their Lambda functions. Although implementing security at a granular level can be a tedious task, there are additional tools that can help identify vulnerabilities hidden within the codebase. By implementing a transparent security model along with continuous monitoring of cloud functions, developers can help keep applications running on AWS Lambda secure.
Next steps
As the attack surface grows more complex, it’s essential to take stock of where we stand, and what we need to do better. Check out the following resources to help you maintain cyber hygiene and stay ahead of the threat actors: