How To Enhance Information Security Using Power Of Lambda

Meenal Kekre
11 min readJun 9, 2021

--

Power Of Lambda

Introduction

We all know how easy it is to build web application on cloud. But the most challenging part, which is highly discounted, is the security aspect of it.

To enunciate, let’s take an example of how Vietnamese won the war against giant US army? It was simply made possible using Guerilla tactics, that is attack through anywhere when it is least expected and having the element of surprise to one’s advantage.

Now, think of a well-structured fort with robust stone walls, harvesting skilled warriors and enough weapons to hold the fort. But if the doors are left unguarded, anyone can attack from anywhere and the fort can be conquered.

Web applications on cloud are equally vulnerable, no matter how hard you secure the services and communication, even a small loop whole causes a big security breach.

The purpose of this paper is not to scare you with possible security blunders but to relieve your worries of unexpected attacks, with advanced solution provided by AWS in the form of serverless lambdas, which plugs the loopholes to make new age Guerilla attacks futile.

Web applications expose data in the form of digital assets like images, files, or APIs. Therefore, it is crucial that only authenticated and authorized users have access to the critical information. This way one can protect the application information against malicious attacks.

In AWS, APIs are exposed through API Gateway, and the web content is exposed through CloudFront service. The best approach to achieve the desired protection, is to validate each incoming request at these gateways, and check if it is coming from authenticated source with valid status, and if the requestor is eligible to fulfill the action. Custom authorizer Lambda is used to intercept all incoming requests for API Gateway, similarly Edge Lambda is used to intercept all requests for CloudFront

This paper will be useful for any developer, particularly novice, who is working with AWS applications.

Here, I have explained how to secure the access to web application using Custom Authorizer lambda and Edge lambda, covering the important fundamentals, detailed configuration, and automated deployment.

1. What is Custom Authorizer?

Custom authorizer is a feature provided by API Gateway to separate authorization logic from the business logic. This is particularly powerful when the application is split across many functions in multiple services. Authentication and authorization can happen in a separate function, allowing your other microservices to focus on their core responsibilities. This fits well with decoupled, microservice architectures.

Custom Authorizer provides

· Granular access needs, role-based access control

· Resource-based access limitations — only the owner of a resource can update or delete the resource

· Throttling- limit the access for requests coming from specific IP range (VPN) or limit the access for specific time range, or duration

1.1 Authorization Workflow

The workflow of incoming request at API Gateway is explained in detail as per https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-use-lambda-authorizer.html

API Gateway Authorization Workflow

1. The client calls a method on an API Gateway API method, passing a bearer token or request parameters.

2. API Gateway checks whether a Lambda authorizer is configured for the method. If it is, API Gateway calls the Lambda function.

3. The Lambda function authenticates the caller as follows:

· Calling out to an OAuth provider to get an OAuth access token.

· Calling out to a SAML provider to get a SAML assertion.

· Generating an IAM policy based on the request parameter values.

· Retrieving credentials from a database.

4. If the call succeeds, the Lambda function grants access by returning an IAM policy and a principal identifier.

5. API Gateway evaluates the policy.

· If access is denied, API Gateway returns a suitable HTTP status code, such as 403 ACCESS_DENIED or 401 ACCESS_UNAUTHORISED

· If access is allowed, API Gateway executes the method. If caching is enabled in the authorizer settings, API Gateway also caches the policy so that the Lambda authorizer function doesn’t need to be invoked again.

1.2 Configuration

Lambda function containing the authorization logic is configured on API Gateway as Authorizers.

Configure Authorizer in API Gateway

It needs an IAM role configured with following IAM Policies to invoke and execute lambda

{

“Version”: “2012–10–17”,

“Statement”: [

{

“Action”: [

“execute-api:Invoke”,

“execute-api:ManageConnections”

],

“Resource”: “arn:aws:execute-api:*:*:*”,

“Effect”: “Allow”

},

{

“Action”: [

“lambda:InvokeFunction”

],

“Resource”: “*”,

“Effect”: “Allow”

}

]

}

This authorizer is attached to relevant API end point in API gateway under Method Execution

Map Authorizer to route in API Gateway

This way, the authorizer is invoked every time the API endpoint is requested

1.2.1 Types of Custom authorizer

Custom authorizer is of two types, Token based, and Request based.

Selection of proper custom authorizer depends on the requirement to access incoming information from requests like headers, query params, resource, host which is available through event object in the authorizer.

In Token based authorizer, event object is small and simple.

For Token based authorizer, you must specify a custom header as the Token Source when you configure the authorizer for your API. The API client must pass the required authorization token in that header in the incoming request. Upon receiving the incoming method request, API Gateway extracts the token from the custom header. It then passes the token as the authorizationToken property of the event object of the Lambda function, in addition to the method ARN as the methodArn property:

{

“type”:”TOKEN”,

“authorizationToken”:”{caller-supplied-token}”,

“methodArn”:”arn:aws:execute-api:{regionId}:{accountId}:{apiId}/{stage}/{httpVerb}/[{resource}/[{child-resources}]]”

}

In Request based authorizer, API Gateway passes the all the request parameters to the authorizer. The request parameters include headers, path parameters, query string parameters, stage variables, and some of request context variables.

The following example shows an input to a REQUEST authorizer for an API method (GET /request) with a proxy integration:

{

“type”: “REQUEST”,

“methodArn”: “arn:aws:execute-api:us-east-1:123456789012:s4x3opwd6i/test/GET/request”,

“resource”: “/request”,

“path”: “/request”,

“httpMethod”: “GET”,

“headers”: {

“X-AMZ-Date”: “20170718T062915Z”,

“Accept”: “*/*”,

“headerauth1”: “headerValue1”,

“CloudFront-Viewer-Country”: “US”,

“CloudFront-Forwarded-Proto”: “https”,

“CloudFront-Is-Tablet-Viewer”: “false”,

“CloudFront-Is-Mobile-Viewer”: “false”,

“User-Agent”: “…”,

“X-Forwarded-Proto”: “https”,

“CloudFront-Is-SmartTV-Viewer”: “false”,

“Host”: “….execute-api.us-east-1.amazonaws.com”,

“Accept-Encoding”: “gzip, deflate”,

“X-Forwarded-Port”: “443”,

“X-Amzn-Trace-Id”: “…”,

“Via”: “…cloudfront.net (CloudFront)”,

“X-Amz-Cf-Id”: “…”,

“X-Forwarded-For”: “…, …”,

“Postman-Token”: “…”,

“cache-control”: “no-cache”,

“CloudFront-Is-Desktop-Viewer”: “true”,

“Content-Type”: “application/x-www-form-urlencoded”

},

“queryStringParameters”: {

“QueryString1”: “queryValue1”

},

“pathParameters”: {},

“stageVariables”: {

“StageVar1”: “stageValue1”

},

“requestContext”: {

“path”: “/request”,

“accountId”: “123456789012”,

“resourceId”: “05c7jb”,

“stage”: “test”,

“requestId”: “…”,

“identity”: {

“apiKey”: “…”,

“sourceIp”: “…”

},

“resourcePath”: “/request”,

“httpMethod”: “GET”,

“apiId”: “s4x3opwd6i”

}

}

Based on the incoming parameters, request is validated. Sometimes additional information is fetched through network calls and response is returned to API Gateway

1.2.2 Responses

Once the incoming request is validated, authorizer returns principalID, policy and context object

{

“principalId”: “my-username”,

“policyDocument”: {

“Version”: “2012–10–17”,

“Statement”: [

{

“Action”: “execute-api:Invoke”,

“Effect”: “Allow”,

“Resource”: “arn:aws:execute-api:us-east-1:123456789012:qsxrty/test/GET/mydemoresource”

]

},

“context”: {

“org”: “my-org”,

“role”: “admin”,

“createdAt”: “2019–01–03T12:15:42”

}

}

Principal id is the unique identifier

The Policy Document is an IAM policy that allows access to the underlying API Gateway resource that the user is trying to access. It has ‘Action’ which specifies the type of action that can be performed on specific Resource as per below

“Statement”: [

{

“Action”: “execute-api:Invoke”, // ← What action they can take.

“Effect”: “Allow”, // ← Allow the action

“Resource”: “arn:aws:execute-api:us-east-1:123456789012:qsxrty/test/GET/mydemoresource” // ← The resource on which they can perform this action

]

The context object is an optional property where any additional information can be added. This context is added to the event object in the backing Lambda function.

The context object is a helpful tool with custom authorizers. It is used to enrich the request with information about the user from authentication lookup. The information like username, userid, role which is already evaluated at authorizer, can be passed to backend APIs through API Gateway to save extra processing.

The information in context object is mapped to request headers, at API Gateway, in the Integration Request of the API, as shown below

1.2.3 Caching:

Custom authorizer adds to the latency in API Gateway calls. Authorization logic may need extra network calls or database calls to validate. To avoid too many network calls, some info can be cached by authorizer or external caching server like Redis (caching service managed by AWS Elastic cache)

AWS API gateway can cache the response from the lambda authorizer for a given user.

Caching at API Gateway is achieved via Cache Key. This is the way to identify a particular user in your custom authorizer for caching purposes.

For token-based authorizers, the header value that is used for authorizer will be the cache key. Any requests that use the same header within cache expiry will receive the cached result from the authorizer.

For request-based authorizers, the parameter(s) like HTTP headers, query string parameters, as well as stage variables and context from API Gateway can serve as cache key.

1.3 Deployment

In AWS, the deployment is automated using Cloud Formation service.

Custom authorizer is deployed as lambda function and then this function is attached to API gateway as shown below,

#Adding ANY method for /{proxy+}

AnyMethod:

Type: ‘AWS::ApiGateway::Method’

Properties:

RestApiId:

ResourceId: // reference of resource

HttpMethod: ANY

AuthorizationType: CUSTOM

AuthorizerId: // Reference Id of Authorizer

RequestParameters:

method.request.path.proxy: true

Integration:

Type: HTTP_PROXY

ConnectionType: VPC_LINK

ConnectionId: ${stageVariables.vpcLink}

PassthroughBehavior: WHEN_NO_MATCH

Uri:

IntegrationHttpMethod: ANY

TimeoutInMillis: 29000

RequestParameters:

integration.request.path.proxy: method.request.path.proxy

integration.request.header.x-accountid: context.authorizer.tid

integration.request.header.x-user-role: context.authorizer.user_role

integration.request.header.x-userid: context.authorizer.uid

IntegrationResponses:

- StatusCode: 200

ResponseTemplates:

“application/json”: “null”

MethodResponses:

- StatusCode: 200

ResponseModels:

“application/json”: “Empty”

Where ‘AuthorizationType’ is ‘CUSTOM’ and ‘AuthorizerId’ is the reference of custom authorizer lambda function.

To deploy lambda function, most popularly used deployment frameworks are Serverless or AWS SAM, these frameworks are built on top of CloudFormation

SAM is specific to AWS, whereas Serverless can be extended to Azure, GCP, IBM OpenWhisk, CloudFlare, Kubeless, Fn, and Spotinst. It also has a number of useful built-in commands such as ‘the ability to execute functions locally’ and to tail function logs from the CLI.

Serverless template to deploy authorizer lambda is as below

service: authorizer

provider:

name: aws

runtime: nodejs12.x

stage: ${opt:stage}

region: ${opt:region}

deploymentBucket:

maxPreviousDeploymentArtifacts: 1

blockPublicAccess: true

role: // Role of IAM user

functions:

auth:

handler: index.handler // handler method of lambda function

Resources:

#IAMRole for Serverless

ServerlessRole:

Type: AWS::IAM::Role

Properties:

RoleName: authorizer-service-role-${opt:stage}

AssumeRolePolicyDocument:

Version: ‘2012–10–17’

Statement:

- Effect: Allow

Principal:

Service:

- lambda.amazonaws.com

Action:

- sts:AssumeRole

#IAMRole Policy for Serverless

ServerlessPolicy:

Type: AWS::IAM::Policy

Properties:

PolicyName: authorizer-service-policy-${opt:stage}

PolicyDocument:

Version: ‘2012–10–17’

Statement:

- Effect: Allow

Action:

- logs:CreateLogGroup

- logs:CreateLogStream

- logs:PutLogEvents

Resource: “*”

- Effect: Allow

Action:

- ec2:DescribeNetworkInterfaces

- ec2:CreateNetworkInterface

- ec2:DeleteNetworkInterface

- ec2:DescribeInstances

- ec2:AttachNetworkInterface

Resource: “*”

Roles:

- Ref: ServerlessRole

Till now, we saw how the requests coming to application is intercepted at API gateway and authorized using custom authorizer lambda. Similarly, another gateway of incoming requests is CloudFront where the requests to web content are catered.

To enhance security, the requests to fetch or add content should be allowed for users with proper permissions. This is achieved with lambda@Edge

2. What is Lambda@Edge?

Lambda@Edge is an extension of AWS Lambda, a computer service that lets you execute functions that customize the content that CloudFront delivers.

Lambda @edge is configured at CloudFront so that it gets invoked for each incoming request. It has the centralized authorization logic to validate the access for user, user role or other parameters

2.1 Authorization Workflow

The workflow of incoming request is as explained below

CloudFront Incoming Request

1. Each request to upload or download the digital asset passes through CloudFront , intercepted by Edge lambda before performing any action on the origin which can be S3 bucket or a MediaStore container, a MediaPackage channel, or a custom origin, such as an Amazon EC2 instance or HTTP web server.

2. At Edge lambda, the user information is accessed through event object (as defined in 2.2.1). Each request is validated against the permission of the requester be it user, domain, IP or role to allow the action, be it upload or download of the asset

3. If allowed, then the request is passed to origin else the error is passed to requestor.

2.2 Configuration

Lambda functions are used to change CloudFront requests and responses at the following events:

· After CloudFront receives a request from a viewer (viewer request)

· Before CloudFront forwards the request to the origin (origin request)

· After CloudFront receives the response from the origin (origin response)

· Before CloudFront forwards the response to the viewer (viewer response)

The lambda function is configured to be intercepted for each incoming request based on the event.

2.2.1 Event of Edge Lambda

In Edge Lambda, all the information about the Incoming request is accessible through different attributes of event object. Sample of event object is as below

{

“Records”: [ {

“cf”: {

“config”: {

“distributionDomainName”: “domain name”,

“distributionId”: “E22607KTRHWUFL”,

“eventType”: “viewer-request”,

“requestId”: “f59WBL3FuoCsAcBEiqojjA7OdxNZAjcMCjOBot2sKlnkacrR_JnJFA==“},

“request”: {

“clientIp”: “*.*.*. *”,

“headers”: {

“host”: [ {

“key”: “Host”, “value”: “some host”}],

“user-agent”: [ {

“key”: “User-Agent”, “value”: “”

“method”: “OPTIONS”,

“querystring”: “,

“uri”: ““}}} ] }

These attributes are validated against the access rules in lambda function. The valid request is then passed to CloudFront.

2.2.2 CloudFront Mapping

The edge lambda is configured in CloudFront for a particular path pattern as shown

CloudFront Mapping to Origin

The behavior is associated with Edge Lambda as below

Configure Edge Lambda to event in CloudFront

This indicates that the Edge lambda function is triggered by CloudFront events such as requests for content by viewers

2.3 Deployment

Like custom authorizer, Edge lambda function is deployed using Serverless.

The association of lambda with CloudFront is done as shown

functions:

viewerRequest:

handler: lambdaEdge/viewerRequest.handler

events:

- preExistingCloudFront:

# — — Mandatory Properties — — -

distributionId: xxxxxxx # CloudFront distribution ID you want to associate

eventType: viewer-request # Choose event to trigger your Lambda function, which are `viewer-request`, `origin-request`, `origin-response` or `viewer-response`

pathPattern: ‘*’ # Specifying the CloudFront behavior

includeBody: false # Whether including body or not within request

# — — Optional Property — — -

stage: dev # Specify the stage at which you want this CloudFront distribution to be updated

plugins:

- serverless-lambda-edge-pre-existing-cloudfront

auth-Edge:

handler: auth.handler

role: AuthEdgeLambdaRole

memorySize: 128

timeout: 5

package:

exclude:

- node_modules/**

events:

- preExistingCloudFront:

# — — Mandatory Properties — — -

distributionId: ‘${file(./environment/${opt:stage}.json):distributionid}’ # CloudFront distribution ID you want to associate

eventType: ‘viewer-request’ # Choose event to trigger your Lambda function, which are `viewer-request`, `origin-request`, `origin-response` or `viewer-response`

pathPattern: ‘authorize’ # Specifying the CloudFront behavior

includeBody: false

stage: ${opt:stage}

Conclusion:

It is important to authenticate and authorize the access to critical information and protect the applications against malicious attacks. Also, managing and auditing network access is essential to information security. Access should be granted on a need-to-know basis.

AWS provides many security-specific tools and features across network security, configuration management, access control, and data encryption. However lightweight Serverless lambda is best suited to provide centralized access control without adding more latency. Lambda authorization is particularly powerful when the application caters many services, where the services can focus on the business logic while the common authorization norms are enforced by authorization service maintained and deployed independently. It is better to have your authentication and authorization logic federated and packaged to avoid redeployments.

High level of security can be enforced in simple steps with custom authorizer and Edge Lambda that allow set and forget kind of deployment.

References

https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-lambda-authorizer-input.html

https://docs.aws.amazon.com/lambda/latest/dg/lambda-edge.html

https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/DownloadDistS3AndCustomOrigins.html#concept_CustomOrigin

--

--