This is the second in a series of posts about best security practices for serverless applications running in AWS. Serverless applications free you from much of the pain and complication that comes with traditional infrastructure management, which can get your product to market much faster. They also come with some unique security considerations.
In the previous installment, we discussed how to secure the Lambda function execution environment itself, and now we move on to AWS endpoint security.
Your application almost certainly exposes HTTP endpoints, and serverless endpoints have many of the same security concerns as legacy endpoints. Thankfully, AWS boasts a variety of services to help you secure your transport layer and offload authentication logic, as well as providing multiple options for exposing your Lambda functions to HTTP events in the first place.
Let’s discuss how to choose a technology for exposing your Lambda functions, how to get free and secure TLS/SSL certificates from AWS, and how to separate authentication and authorization logic from your business logic with custom authorizers.
The internet is rapidly moving away from insecure HTTP connections in favor of encrypted connections over HTTPS. Public HTTPS connections require certificates signed by a recognized Certificate Authority. These were once expensive and difficult to provision and manage, which was a significant barrier to the widespread adoption of secure HTTP connections.
There are now several providers of free SSL/TLS certificates, including the very popular Let’s Encrypt, but the AWS Certificate Manager (ACM) is your best choice for generating and managing certificates in AWS.
Certificate Manager will issue free public certificates for any domain you control, and even supports wildcards. You can validate that you own a domain by creating a DNS record, or with an email to the domain’s publicly registered owner. If your DNS is hosted with Route 53, Certificate Manager will even create the DNS records for you automatically.
Most importantly, Certificate Manager will automatically renew and rotate your certificates for you, and deploy them when they’re renewed. When you configure an Application Load Balancer or an API Gateway custom domain, you can simply specify an ACM certificate and AWS takes care of the rest. Once it’s set up, you never have to worry about your certificates expiring again.
There are two options for triggering your Lambda functions with HTTP requests. Until recently, API Gateway was the only option, but AWS includes support for Lambda functions as targets for Application Load Balancers as well. Both options support TLS/SSL with ACM certificates (in fact, API Gateway requires it).
ALB Integration with Lamba | API Gateway |
---|---|
Less features | Many features and options (rate limiting, automatic SDK generation, request validation, etc.) |
Straightforward & simple | Enhanced security (custom authorizers) |
Cheaper per request | More expensive per request |
ALB integration with Lambda is straightforward and relatively bare-bones in terms of features when compared with API Gateway. API Gateway is a feature-rich service with many options, and it provides two different endpoint types with different feature sets and price points, depending on your requirements and budget.
ALBs are cheaper per request than either API Gateway option, even with tiered pricing and the newer, cheaper HTTP API type. API Gateway still has a significant security advantage, however, in the form of custom authorizers.
If you need features like rate limiting, automatic SDK generation, request validation and transformation, or the authorizer features we’re about to discuss, API Gateway is a great choice, if it fits in your budget.
If you implement those features yourself or don’t need them, or if you just want to add some Lambda functions to an existing web app that already sits behind an ALB, direct ALB integration might be a better choice.
One of the biggest security advantages to using API Gateway is Authorizers. Authorizers let you separate your authentication and authorization logic from your business logic, by essentially delegating those decisions to API Gateway before your business logic is invoked.
Unauthorized requests are gated before they reach your API, so your business logic can simply retrieve the authorized principal from the request context and execute, knowing it can not be invoked in an unauthorized context. This makes for cleaner code and very neat separation of concerns.
API Gateway can be configured to use AWS-provided authorizers for Cognito User Pools, IAM Roles, and JWTs from OpenID Connect / OAuth 2 identity providers, or you can even write your own authorizer by specifying an arbitrary Lambda function.
Support for different authorizer types varies depending on what kind of endpoint type you have configured in API Gateway.
If you’re already using Cognito User Pools for your application, AWS provides an integration with API Gateway that just works out of the box. This is the only authorizer option that is available for both HTTP and REST API types.
API Gateway will inspect an HTTP header for the token issued by Cognito, verify its signature, and check that it’s still valid before invoking your Lambda function. If it’s not, it will automatically redirect the user to your Cognito auth flow.
AWS provides detailed instructions for configuring the authorizer here, but the process is essentially as simple as specifying a User Pool, and which HTTP header to check for the bearer token.
In this setup, the authorizer is just checking to see if the user is a valid user in your pool, and the Lambda function is invoked with its normal execution role. It does not give you a way to differentiate between different types of users and assign finer-grained permissions. For that kind of fine-grained authorization, you’ll need to use one of the following other options.
API Gateway also supports IAM-based authorization, by integrating with Cognito Federated Identities (also confusingly referred to as Cognito Identity Pools). This type of authorizer is only supported if you’re using a REST endpoint type with API Gateway.
Like the User Pools authorizer, the client first authenticates with Cognito and receives a JWT. Instead of passing that token directly to API Gateway and making a request, however, the token is sent to Cognito Federated Identities and exchanged for a set of temporary IAM credentials, which can be attached to any IAM policy you care to specify. Cognito Federated Identities allows you to configure which IAM roles are assigned to which users in your pool.
Those IAM credentials are then used to make requests to API Gateway by signing the requests with Signature V4, which is the same request signature used for all other AWS APIs. When API Gateway receives a request with this signature, it knows what IAM role is making the request, and it can check what policies are associated with that role.
Since each request now has a specific IAM role, you can use this method to implement fine-grained access control by allowing or denying permission to invoke specific Lambda functions in the IAM roles assigned to your users.
A custom IAM policy used in this method that provides finer-grained access control might look like:
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "execute-api:Invoke",
"Effect": "Allow",
"Resource": "arn:aws:execute-api:*:*:ff5h7fdsed/*"
},
{
"Action": "execute-api:Invoke",
"Effect": "Deny",
"Resource": "arn:aws:execute-api:*:*:ff5h7fdsed/*/POST/inventory"
}
]
}
In this example, the user is allowed to access any operation in the API except POSTs to /inventory
.
If you’re not using Cognito, or if you need to implement custom logic, API Gateway also provides the option of specifying your own Lambda Authorizer. Like the IAM-based authorizer above, this option is only available for REST endpoint types in API Gateway.
(Note: AWS previously referred to these as “Custom Authorizers”, and you may still see documentation using the terms interchangeably.)
Lambda Authorizers are Lambda functions that API Gateway will invoke before your business logic to perform authentication and authorization. They have the advantage of allowing you to authenticate against any identity provider (IdP), or implement any custom authentication scheme of your choice, as long as your identity provider issues a bearer token you can send to API Gateway when making requests.
Before invoking your custom authorizer, API Gateway will inspect an HTTP header of your choice for the bearer token, and check its own local cache for a known valid policy associated with that token. If it finds no policy cached, it will invoke your custom authorizer.
Your custom authorizer function examines the HTTP request and can either reject it outright as unauthorized, or accept the request and return an IAM policy document that will be used to invoke the underlying Lambda function. Much like the Federated Identities example, this IAM policy document can be used for finer-grained access control.
HTTP endpoint types in API Gateway are an attempt by AWS to simplify the vast array of (often unneeded) features provided by the older REST endpoint type. If you’re not going to use the Cognito User Pool authorizer with an API of this type, your other option is to use native support for OpenID Connect (OIDC) or OAuth 2.0 to secure your API.
AWS refers to this as “JWT Authorizers”, and the flow is similar to the Cognito User Pool flow above, except that instead of using Cognito as the IdP, API Gateway validates the JWT using the public key fetched from the issuer’s jwks_uri
, and then checks that the various claims in the token are valid. If any of the checks fail, API Gateway denies the request.
After the token is validated, API Gateway will take all the claims in the token and pass them to the Lambda backend. For example, if the validated token includes an emailID
claim, that will be available in the Lambda context as $context.authorizer.claims.emailID
.
Once you’ve picked a strategy for exposing your AWS endpoints, secured them with TLS/SSL, and wired up an authentication system, the next step is to secure your application against common attacks.
AWS provides tools like the Web Application Firewall (WAF) and CloudFront that can help you fend off cross-site scripting (XSS), SQL injection, distributed denial of service (DDoS), and other common attack types. We’ll dive into those in the next installment of this series on serverless best practices.
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.