Security reference architecture for a serverless application

Serverless does not mean no servers

Demo Application

  1. Vulnerability scanner services like Nessus send a POST request with vulnerability data to the Route53 domain name which aliases to the API Gateway custom domain name
  2. API Gateway triggers POST Lambda function which processes and stores the data into DynamoDB
  3. Customers of the vulnerability data send a GET request with their org id as parameter to the Route53 domain name
  4. API Gateway triggers GET Lambda function which fetches the data for that org id from DynamoDB table and returns to the customer
Application architecture for demo serverless application

Security Integrations


  • In the demo application, authentication is enabled via digital certificates using mutual TLS (mTLS). A root CA certificate is configured as part of API Gateway in order to authenticate client certificates. This is a good option if you have clients coming from a different cloud environment (cross-cloud traffic) or even on-premises. Some best practices around using this setup for production — client/server certificates must be short-lived so automatically creating and supplying it to all parties is critical from an operational standpoint. Also, private keys must be stored in a secure storage like Vault.
Authentication via mTLS
  • If you have clients using the cloud provider’s identity e.g. an EC2 instance with an attached IAM role, then an easy option to authenticate is via cloud service provider’s native IAM system.
Authentication via native cloud provider identity
  • Authentication is also possible via OAuth Client Credentials Flow where the client authenticates with an authorization server using client credentials, gets an access token, and then calls the API Gateway with the access token.This would also be useful in cases were API customers may need to access the API over a Web UI:
Authentication via OAuth Client Credentials Flow


  • In the demo app, after mTLS authentication is successful, authorization happens via Lambda functions that validate if the OU (Organization Unit) in the certificate is authorized to perform the specific API method on the specific resource. This mapping is made accessible to the Lambda function via a DynamoDB table.
Authorization via OU field in client certificates

Infrastructure Least privilege

  • API Gateway is only allowed to invoke the specific downstream Lambda functions and the authorizer Lambda functions by assigning the following IAM policy Terraform resource to the API Gateway IAM role:
Using least privilege on IAM role
  • The AWS IAM role for the Lambda function to respond to the GET API only has permissions — “dynamodb:GetItem” and “dynamodb:Query” on the specific DynamoDB table:
Using least privilege on IAM role
Using least privilege on IAM role

Network Security

  • In the demo application, the endpoint is public but it is restricted to be only accessed from my laptop ip address using resource policy on the API Gateway as shown below:
Restricting network access to a API Gateway endpoint with a source ip address
  • An alternative to above would be use AWS WAF for IP filtering:
  • Also, you don’t need to expose API Gateway to public, there is an option with AWS to only create a private API Gateway endpoint. You may use AWS private link to connect to a private API Gateway endpoint from another AWS account.
  • With AWS API Gateway, you can choose a minimum Transport Layer Security (TLS) protocol version to be enforced for your Amazon API Gateway custom domain by setting a security policy. A security policy determines two things: 1) The minimum TLS version that API Gateway uses to communicate with API clients. 2) The cipher that API Gateway uses to encrypt the content that it returns to API clients
Enforcing a minimum security policy with AWS API Gateway custom domain
  • For AWS, some of the resources where this capability exists and should be watched out for are very well explained in this open-source Git repo — AWS Exposable Resources
  • In the demo app, VPC private endpoints are used for accessing S3 and DynamoDB. Lambda functions are connected to the VPC with a security group in order to access S3 and DynamoDB with a private VPC endpoint.
  • VPC Endpoint policy is set up to only allow network access to a specific resource via the private endpoint.
VPC endpoint policy for DynamoDB to be accessed via Lambda

Code Security Hygiene

  • In the demo app, API Gateway is used for validation of request body and request parameter by configuring it in the openapi.yaml file:
openapi.yaml file showing input validation
  • Request body passthrough is specifically set to NEVER which is needed in cases where the request body payload cannot be validated, so that unsupported content types fail with 415 “Unsupported Media Type” response.
  • To prevent code vulnerabilities flowing to downstream systems, proper encoding is performed on all output. E.g. by doing return json.dumps(output) prevents JSON injection attacks in downstream
  • AWS enables some default throttling limits for API Gateway so if that works for your design, you might not have to do this yourself.
  • To set throttling limits for each of your API methods, you may create a Usage Plan
  • See example of how DynamoDB injection can work →NoSQL Injection: DynamoDB
  • The demo app uses boto3 Dynamodb APIs to query the database and does not create dynamic queries by concatenating input strings.

Data Protection

  • In the demo app, for the centralized management of key access policies, AWS IAM is enabled to help control access of the CMK. This is done by assigning key permissions to the root AWS user — arn:aws:iam::${aws:PrincipalAccount}:root}. Assigning kms:* to the above AWS user is “not secure” as it allows any IAM role in the same account with proper IAM permissions to perform sensitive key admin activities. Therefore, the KMS policy below specifically DENIES sensitive privileges like key deletion and only allows these sensitive actions to be done by select few IAM roles:
Secure AWS KMS access policy
  • Automatic key rotation is enabled for the CMK based on the organizational key rotation policy


  • Below is an example of how Authorization logging can be achieved with Python:
Example of Authorization logging with Python
  • For AWS, CloudTrail provides the activity logs but be careful of the default configuration as it does not log all data access events. Example, by default CloudTrail would log S3 Bucket-level calls which include events like CreateBucket, DeleteBucket, PutBucketLifeCycle, PutBucketPolicy, etc, but it does not log object-level Amazon S3 actions like GetObject, PutObject, DeleteObject, etc. You can specifically enable data access logging for Lambda, S3 and DynamoDB when you are creating the CloudTrail using the event_selector data_source:
Using CloudTrail to log specific data events in Lambda, S3 and DynamoDB
  • Also depending on which AWS services you might be using, you will need to enable data access logs for those services on a per-resource basis. AWS documentation for some services like API Gateway, NLB and Cloudfront are linked below:

Concluding Notes



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