Skip to content
Anthony DiMarcoJun 2, 2020 12:00:00 AM6 min read

Security Best Practices for Serverless Applications on AWS

Modernizing Security: AWS Series - Security Best Practices for Serverless Applications on AWS

 

Modernizing Security: AWS Series - Security Best Practices for Serverless Applications on AWS

Security Best Practices for Serverless Applications on AWS

Serverless software architecture is one of the more exciting trends in modern software development. It’s easier to write secure applications when you no longer have to worry about security patches or OS updates. Serverless applications decrease time to market and fuel innovation by letting developers focus on writing code and solving business problems instead of worrying about infrastructure. This shift in responsibility makes some existing best practices for cloud security irrelevant, but it also means that some new best practices are necessary.

This is the first in a series of posts that will examine best practices for securing Serverless applications running on AWS Lambda. In this entry, we will focus on best practices for securing Lambda functions and managing the secrets they use.

Lambda and Shared Responsibility

Most Serverless applications on AWS execute in Lambda. Lambda is a managed runtime environment that hides hardware and OS/platform details from the user. You provide executable code, and Lambda is responsible for the whole stack beneath it when it runs. This changes the Shared Responsibility Model in significant ways.

Shared responsibility model for Lambda

 

Shared responsibility model for Lambda

In this new model, OS and network configuration, platform management, and some fundamental encryption features are now firmly in the AWS-managed “security OF the cloud” and no longer yours to worry about.

Lambda functions are only executed via the AWS Lambda service API, so there is no direct inbound network access, and thus no need to manage inbound network rules.

If you don’t have to worry about infrastructure, filesystem, hardware, or inbound network security, what should you focus on to make your app secure? Some good first steps are:

We’ll touch on both of these in this post. Later posts in this series will cover a Serverless approach to input validation, user authentication, auditing and logging, and how to use automation to tie it all together.

Securing Lambda Functions

Lambdas are event-driven. In simplified form:

Lambdas are event-driven

The left and right arrows are both secured with IAM policies. On the left, events that are authorized to trigger the function are specified with a Resource Policy. On the right, an Execution Role constrains what the Lambda function is authorized to do.

IAM

Identity and Access Management (IAM) is the heart of security for Lambda. IAM policies are often brushed over with little attention by developers, and this inattention is a common cause of security vulnerabilities. We’ll dive deep into technical detail on IAM best practices in a couple future posts.

IAM policies should follow the principle of least privilege, which means granting only the permissions that are necessary to perform your task. Limit your policy to the specific actions and resources it needs - e.g. your user signup function will need permission to write the users table, but probably not to read or write your inventory table.

Resource Policy

Decrease your attack surface by granting permission to execute your Lambda function only to the services that should execute it. For example, if your function should only be triggered by SNS, you can ensure that by specifying it in the resource policy. An example might look like:

{
   "Sid":"sns",
   "Effect":"Allow",
   "Principal":{
      "Service":"sns.amazonaws.com"
   },
   "Action":"lambda:InvokeFunction",
   "Resource":"arn:aws:lambda:us-east-
2:123456789012:function:my-function"
}
Execution Role

Your functions will almost certainly need permissions to call other AWS APIs. The best way to grant those permissions is to use the function’s Execution Role. Don’t hard-code IAM keys, or pass them to your function in environment variables. When the Lambda service invokes your function, it assumes the execution role and the permissions you specify in it.

This approach decouples your source code from its own security posture. If a developer tries to change the code to use a service it shouldn’t have access to, or an attacker somehow triggers malicious behavior in your function, the attempt will fail due to the constraints in the execution role.

Since you no longer generate IAM keys, this approach also means you don’t have to manage key rotation, and there’s no opportunity for the keys to leak since they were never generated in the first place. AWS will generate temporary credentials for your function with STS every time it executes.

Database Permissions

You can also use IAM to authenticate directly with most popular RDS databases, instead of generating (and managing, and rotating) a username/password. Grant this access to your function in your execution role along with your other permissions.

An example policy statement looks like:

{
    "Effect": "Allow",
    "Action": "rds-db:connect",
    "Resource": "arn:aws:rds-db:<REGION>:
<AWS_ACCOUNT_ID>:dbuser:
<DB_RESOURCE_ID>/<DB_USERNAME>"
}

You’ll need to update your application code to get a temporary db token with the SDK for RDS. This is a simple procedure, and you can find more detail here.

Avoid “monolambdas”

A common antipattern in Lambda development is to create a single Lambda function that contains all of your logic - essentially re-creating a monolithic application inside Lambda. If you’re building a RESTful API and your routing logic lives inside your Lambda instead of in API Gateway or an ALB, you’re headed down this path of muddled permissions.

When all your logic lives in a single Lambda, its IAM policy contains all the permissions for all of your application. Instead, write single-responsibility functions that need fewer, more narrowly scoped permissions.

Storing Secrets Securely

When you need secrets, or credentials that you can’t grant directly with your execution role, it’s important to store and access them securely. Depending on your requirements, good managed options include:

  • Secrets Manager
  • Systems Manager Parameter Store (with secure strings)
  • Lambda environment variables encrypted with a KMS key

For a detailed comparison of secrets managers on AWS, see our previous blog post here.

ScaleSec blog: A Comparison of Secrets Managers for AWS

 

ScaleSec blog: A Comparison of Secrets Managers for AWS

Programmatic Access

Secrets Manager and Parameter Store both provide API-based access to secrets, and an audit trail of when those secrets were changed and by whom. Secrets Manager is slightly more expensive, but can also automatically handle key rotation for supported AWS services, or custom key rotation if you provide a Lambda for it to execute.

To use this approach, your Lambda’s execution role will need access to the relevant APIs, plus permissions to decrypt the keys with KMS. You’ll also need to update your function code to retrieve its secrets from these APIs at runtime.

Encrypted Environment Variables

Lambda encrypts environment variables at rest by default, with an AWS-managed customer master key (CMK). You can also configure Lambda to use a key you provide instead of the default key if your organization has requirements to manage encryption keys and control when they’re rotated.

When you provide your own key, only users in your account with access to the key can view or manage environment variables for your function. Users without those permissions can still manage the Lambda function, they just can’t view or manage its environment variables.

Conclusion

Hopefully this has given you some useful information on where to start securing your serverless applications. Securing your Lambdas and secrets is just the start of serverless security, though. In future posts, we’ll cover serverless ways to mitigate risk from SQL injections and cross site scripting, security benefits of API Gateway vs ALBs for exposing endpoints, how to avoid dependency vulnerabilities efficiently by using Lambda layers, various automation options to tie it all together repeatably, and a lot more.

Read about Endpoint Security on the next installment of Security Best Practices for Serverless Applications in AWS.


Connect with ScaleSec for AWS business

ScaleSec is an APN Consulting Partner guiding AWS customers through security and compliance challenges. ScaleSec’s compliance advisory and implementation services help teams leverage cloud-native services on AWS. We are hiring!

Connect with ScaleSec for AWS business.

RELATED ARTICLES

The information presented in this article is accurate as of 7/20/23. Follow the ScaleSec blog for new articles and updates.