Installation
  • 30 Apr 2024
  • Dark
    Light

Installation

  • Dark
    Light

Article Summary

Prerequisites

The HUMAN AWS Lambda@Edge Enforcer requires basic knowledge of and access to:

Regional Restrictions

The HUMAN AWS Lambda@Edge Enforcer is accessible globally with full support for all regions supported by Lambda@Edge. You can seamlessly utilize our solution without any regional restrictions.

AWS Permissions

An AWS account with necessary permissions for CloudFront, Lambda, CloudWatch, and IAM services is required, including the ability to create or modify IAM roles. Root privileges are not required for deployment or operation of the integration.

Usage of AWS Lambda Functions

This enforcer integration leverages AWS Lambda functions, which are billable services according to your agreement with Amazon. There is no need to change current set service limits in relation to HUMAN added Lambda functions. Information about Lambda pricing and Lambda@Edge quotas can be found on the AWS website.

Overview

The {{variable.HUMAN Security}} AWS Lambda@Edge Enforcer consists of three Lambda functions.

  • HumanEnforce - Required. This Lambda is integrated at either the viewer-request or the origin-request event on all routes that should be protected by HUMAN.
  • HumanFirstParty - Optional. This Lambda is integrated at the origin-request event and should be integrated only if a first-party integration is desired. This Lambda will be invoked only for HUMAN's first party requests and will not apply to typical website traffic.
  • HumanActivities - Optional. This Lambda is integrated at the origin-response event and should be integrated only when features or products requiring response data are enabled.

Getting Started

  1. If you don't already have a CloudFront distribution, create one by following the instructions here.

  2. Install the {{variable.HUMAN Security}} Enforcer NPM package into your Lambda project. If you don't have a Node.js project, we recommend using our prebuilt project template, which is a single NPM project that contains all the HUMAN Lambda functions needed for the enforcer.

# if you want to use the template project with the enforcer already built-in
git clone https://github.com/PerimeterX/aws-lambda-edge-template

# if you want to install the enforcer into your existing project
npm i --save @humansecurity/aws-lambda-edge-enforcer

Installing the HumanEnforce Lambda

1. Define the enforcer configuration.

At a minimum, the enforcer configuration must contain your application ID, authentication token, and cookie secret.

Since you may need more than one Lambda function for your integration, we recommend defining your enforcer configuration in a separate file (e.g., config.ts) so that it can be imported as needed across your project.

// config.ts
import { HumanSecurityConfiguration } from '@humansecurity/aws-lambda-edge-enforcer';

// define configuration
const config: HumanSecurityConfiguration = {
    px_app_id: '<APP_ID>',
    px_auth_token: '<AUTH_TOKEN>',
    px_cookie_secret: '<COOKIE_SECRET>',
};
AWS Secrets Manager

The cookie secret and auth token configurations are sensitive. While it is possible to hard-code these strings in the code or environment variables, best practice is to store sensitive tokens using AWS Secrets Manager. See here for more information on how to use the AWS Secrets Manager with Lambda@Edge.

2. Create the HumanEnforce Lambda code.

For an out-of-the box request handler with the HumanSecurityEnforcer integrated into it already, simply import and use the createHumanEnforceHandler function.

// index.ts
import { createHumanEnforceHandler } from '@humansecurity/aws-lambda-edge-enforcer';
import { config } from './config';

// create and export the handler
export const handler = createHumanEnforceHandler(config);

For a more customized solution, import the HumanSecurityEnforcer class and use it in your handler as desired.

The recommended usage is to:

  • Initialize the enforcer with a configuration object. (This can be done either inside or outside the handler function.)
  • Call the enforce() function inside the Lambda handler and await the result.
  • If the result is not null, return it. If it is null, return the request instead.
// index.ts
import { CloudFrontRequest, CloudFrontRequestEvent, CloudFrontResponseResult, Context } from 'aws-lambda';
import { HumanSecurityEnforcer } from '@humansecurity/aws-lambda-edge-enforcer';
import { config } from './config';

// initialize the enforcer
const enforcer = HumanSecurityEnforcer.initialize(config);

// define a handler
export async function handler(
    event: CloudFrontRequestEvent,
    context: Context
): Promise<CloudFrontRequest | CloudFrontResponseResult> {
    // extract the request from the event
    const request = event.Records[0].cf.request;

    // call enforce() and await the response
    // request is a required argument, context is optional
    const result = await enforcer.enforce(request, context);
    
    // return the result if it exists
    if (result) {
        return result;
    }
    
    // execute your custom logic on the request here
    
    // eventually return the request
    return request;
};
Callbacks vs. Async/Await

In accordance with AWS Lambda's best practices, the HUMAN AWS Lambda@Edge Enforcer library is written using async/await syntax. However, integrating the enforcer into a callback-based handler can be done by calling .then() on the returned Promise rather than awaiting it.

If using callbacks, it's also recommended to set the context.callbackWaitsForEmptyEventLoop property to false for best performance.

export function handler(event, context, callback) {
    context.callbackWaitsForEmptyEventLoop = false;
    const request = event.Records[0].cf.request;
    enforcer.enforce(request, context).then((result) => {
        if (result) {
            callback(null, result);
        } else {
            // continue processing until eventually...
            callback(null, request);
        }
    });
};

3. Package and upload the code to the HumanEnforce Lambda Function.

Bundle the source code so that it can be deployed to the Lambda. For example, you can use esbuild to compile the index.ts file in the example above to ./dist/index.js.

esbuild ./index.ts --bundle --minify --platform=node --target=es2022 --outfile=dist/index.js

Then, follow the instructions here for creating a new Lambda@Edge function in the AWS Console if you don't have one already.

Recommended Name: HumanEnforce
Runtime: Node.js 20.x (or 18.x)
Architecture: x86_64

The default execution role must include the Basic Lambda@Edge permissions at a minimum. If an existing role includes these permissions, that role can be selected. Otherwise, create a new role with the Basic Lambda@Edge permissions and assign it a name (e.g., LambdaEdgeRole).

Screenshot 2024-02-08 at 14.02.57.png

Once the Lambda function is created, the packaged code can be uploaded either by copying and pasting the code into the Lambda function in the AWS Console or by uploading a zip file.

4. Deploy the HumanEnforce Lambda Function to CloudFront.

Follow the instructions here (Lambda console) or here (CloudFront console) to trigger the Lambda Function on a CloudFront event. The Lambda function should be associated with the default cache behavior (or whichever cache behaviors correspond to the paths HUMAN should protect).

CloudFront Event: viewer-request
Include Body: Yes

Screenshot 2024-02-08 at 14.11.01.png

About CloudFront Events

The HumanEnforce Lambda Function can be triggered at either the viewer-request event or the origin-request event. We recommend integrating at the viewer-request event, as it enables HUMAN to protect both cached and uncached content. (For more information, see here and here.)

5. Configure the Origin Request Policy.

Origin request policies control which request headers pass from the viewer-request event to the origin-request (and to the origin itself). If you are integrating the HumanEnforce Lambda at the origin-request event, or if you are utilizing products or features that add headers to the incoming request (e.g., Credential Intelligence), use one of the following policies to ensure that important headers are not removed:

Screenshot 2024-02-08 at 14.26.40.png

Installing the HumanFirstParty Lambda

The following steps are required only if adding support for First Party requests.

1. Define the enforcer configuration.

If possible, this should be the same configuration defined for the HumanEnforce Lambda. At minimum, the configuration object should contain the application ID. (The authentication token and cookie secret are NOT required for the HumanFirstParty Lambda.)

// config.ts
export const config = {
    px_app_id: '<APP_ID>',
};

2. Create the HumanFirstParty Lambda code.

Simply import and use the createHumanFirstPartyHandler function. All customizations for first party requests should be specified in the configuration.

// index.ts
import { createHumanFirstPartyHandler } from '@humansecurity/aws-lambda-edge-enforcer';

import { config } from './config';

export const handler = createHumanFirstPartyHandler(config);

3. Package the code and upload it to the HumanFirstParty Lambda Function.

Bundle the code so and create the Lambda function. Follow the instructions here for creating a new Lambda@Edge function.

Recommended Name: HumanFirstParty
Runtime: Node.js 20.x (or 18.x)
Architecture: x86_64

Once the Lambda function is created, the packaged code can be uploaded either by copying and pasting the code into the Lambda function in the AWS Console or by uploading a zip file.

4. Create the HumanFirstParty Cache Policy.

Follow the instructions here to create a cache policy. Apply the following settings

FieldValue
NameHumanFirstPartyCachePolicy
Minimum TTL0
Maximum TTL10
Default TTL5
HeadersInclude the following headers: User-Agent, Host
Query StringsAll
CookiesInclude the following cookies: *_px*, px**

Screenshot 2024-02-08 at 15.47.39.png

5. Create the cache behavior in the CloudFront distribution for first party requests.

Create a new cache behavior in the CloudFront distribution for first party requests. Apply the following settings:

FieldValue
Path Pattern/<APP_ID_WITHOUT_THE_FIRST_2_LETTERS>/* (e.g., if your application ID is PX12345678, the path pattern would be /12345678/*)
Viewer Protocol PolicyHTTP and HTTPS
Allowed HTTP MethodsGET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE
Cache PolicyHumanFirstPartyCachePolicy
Origin Request PolicyAllViewerExceptHostHeader
Function AssociationsEvent: Origin Request
Function Type: Lambda@Edge
Function ARN/Name: The ARN of the published HumanFirstParty Lambda function (e.g., arn:aws:lambda:us-east-1:<AWS_ID>:function:HumanFirstParty:<VERSION>
Include Body: Checked

Screenshot 2024-02-08 at 16.22.52.png

Screenshot 2024-02-08 at 16.22.14.png

Note that applying the HumanFirstParty Lambda function association to the CloudFront cache behavior deploys the Lambda function to the CloudFront distribution.

Cache Behaviors for Custom First Party Endpoints

If the enforcer was configured to use custom first party endpoints, you must create additional cache behaviors for the custom first party path patterns. Otherwise, requests using the custom paths will not be handled properly!

Installing the HumanActivities Lambda

As part of Credential Intelligence, the HumanActivities Lambda can send the additional_s2s activity to HUMAN on the origin-response event, allowing HUMAN to analyze data related to the response and not just the request. This includes information about the returned HTTP status code and whether an attempted login was successful.

Installing the HumanActivities Lambda simplifies integration of the Credential Intelligence product, but it is not mandatory. The required additional_s2s activities may also be sent manually from the origin server or at a later stage in the request flow. See the px_additional_s2s_activity_header_enabled configuration for more information.

1. Define the enforcer configuration.

If possible, this should be the same configuration defined for the HumanEnforce Lambda. At a minimum, the enforcer configuration must contain your application ID, authentication token, and cookie secret. If being used to send the Credential Intelligence additional_s2s activity, ensure that px_automatic_additional_s2s_activity_enabled is set to true.

// config.ts
import { HumanSecurityConfiguration } from '@humansecurity/aws-lambda-edge-enforcer';

// define configuration
const config: HumanSecurityConfiguration = {
    px_app_id: '<APP_ID>',
    px_auth_token: '<AUTH_TOKEN>',
    px_cookie_secret: '<COOKIE_SECRET>',
    px_automatic_additional_s2s_activity_enabled: true,
};
AWS Secrets Manager

The cookie secret and auth token configurations are sensitive. While it is possible to hard-code these strings in the code or environment variables, best practice is to store sensitive tokens using AWS Secrets Manager. See here for more information on how to use the AWS Secrets Manager with Lambda@Edge.

2. Create the HumanActivities Lambda code.

For an out-of-the box response handler with the HumanSecurityPostEnforcer integrated into it already, simply import and use the createHumanActivitiesHandler function.

// index.ts
import { createHumanActivitiesHandler } from '@humansecurity/aws-lambda-edge-enforcer';
import { config } from './config';

// create and export the handler
export const handler = createHumanActivitiesHandler(config);

For a more customized solution import the HumanSecurityPostEnforcer class and use it in your handler as desired.

The recommended usage is to:

  • Initialize the PostEnforcer with a configuration object. (This can be done either inside or outside the handler function.)
  • Call the postEnforce() function inside the Lambda handler and await it.
  • Return the response.
// index.ts
import { CloudFrontResponseEvent, CloudFrontResponseResult, Context } from 'aws-lambda';
import { HumanSecurityPostEnforcer } from '@humansecurity/aws-lambda-edge-enforcer';
import { config } from './config';

// initialize the postEnforcer
const postEnforcer = HumanSecurityPostEnforcer.initialize(config);

// define a handler
export async function handler(
    event: CloudFrontResponseEvent,
    context: Context
): Promise<CloudFrontResponseResult> {
    // extract the request and response from the event
    const request = event.Records[0].cf.request;
    const response = event.Records[0].cf.response;
    
    // execute your custom logic on the response here
    
    // call and await the postEnforce() function 
    await postEnforcer.postEnforce(request, response);

    // return the response
    return response;
};

3. Package and upload the code to the HumanActivities Lambda Function.

Bundle the code so and create the Lambda function. Follow the instructions here for creating a new Lambda@Edge function.

Recommended Name: HumanActivities
Runtime: Node.js 20.x (or 18.x)
Architecture: x86_64

Once the Lambda function is created, the packaged code can be uploaded either by copying and pasting the code into the Lambda function in the AWS Console or by uploading a zip file.

4. Deploy the HumanEnforce Lambda Function to CloudFront.

Follow the instructions here (Lambda console) or here (CloudFront console) to trigger the Lambda Function on a CloudFront event.

Any CloudFront cache behavior that triggers the HumanEnforce Lambda function should also trigger the HumanActivities Lambda function.

CloudFront Event: origin-response

Screenshot 2024-02-09 at 10.27.20.png


Was this article helpful?

What's Next