Build a CSP report uri endpoint on AWS serverless

Ganapati Sarkar
Jan 1 · 6 min read

The goal of this tutorial is to implement the endpoint for report-uri directive in CSP header. For details regarding CSP in general, please refer to w3c and mozilla documentation.

Content-Security-Policy: ...; report-uri https://e1x3a6m8p4le.execute-api.us-east-1.amazonaws.com/latest;

So what does this report-uri does exactly — Well it specifies an uri (absolute or relative), which accepts a json data over a POST endpoint.

The api can be part of the website code, or setup on a different server. In case the endpoint is under the domain as the website, it can be used as a relative path in your CSP report-uri directive, otherwise an absolute path is needed.

It makes sense to keep this api away from the server where the website is hosted, as any unintentional super restrictive CSP header can send back lots of violation report, and depending on server’s usual traffic it can have multiplicative effect by the factor of N no of violations per Visitor per Page.

That’s why AWS serverless seems perfect for this task. It doesn’t require a whole new server for just one endpoint and the best part is that it can auto scale as per traffic. So lets see how can we set this up on AWS.

To bring it up, we shall be touching these 4 aws services.

  • Lambda
  • API Gateway
  • DynamoDb
  • IAM (to enable above services to talk to each other)

There are multiple ways of creating resources on aws. This article will show the easiest way: aws console, and sometimes later on I will put up a link here for a tutorial on doing the same thing in cloudformation.


DynamoDB

Lets start with creating a dynamodb table. The name of the table can be anything. We will need this name later in our lambda function.

Table name : cspdata
Primary key: id, string

IAM

Before we create the lambda function, it needs to be allowed to access dynamodb. To do this an IAM role is needed, with a policy specifying full access on our dynamo db table.

Role Name: cspdata-lambda-role
Select Lambda and then jump though step 2 and 3
Enter role name, click Create role
Open the role and add an inline policy
Go to JSON tab and paste the below policy.

Paste below policy in policy JSON editor.

Make sure you replace the Account Id, with your own aws account id.

Review and save the policy. The lambda function must use this role in order to access the dynamo db. Do note the wildcard (csp*) in Resource entity above, used to access all tables whose name starts with csp. We can set this to exact table name as well, instead of wildcard.


Lambda

Time to write the lambda function. This function reads the csp report from the request json and writes the data to the dynamodb.

Lambda name: cspdata-lambda
Runtime : NodeJs 8.10
Role : existing, cspdata-lambda-role
Create your lambda function

Make sure correct role is selected, while creating the function.

Replace the default function with the below code

A quick summary of above code:

For every request, if its a POST request, then first replace any empty field values with ‘NA’, check the json body contains ‘csp-report’ in it or not, if yes, then grab the value of ‘csp-report’, append a uuid and timestamp to it and save it in dynamodb and for any non POST request, return error.

In environment variable add a key-value for table.

table : cspdata
set environment variable table as cspdata

Now we need to add a API gateway to create our post endpoint, which on invoke, will call this lambda function.


API Gateway

Create a new rest API Gateway with below details.

name: csp-api

Add a method in that API. In this case it will be a POST endpoint.

Add a new method for POST

Setup the POST endpoint. We need to integrate this endpoint with our lambda function.

Integration type   : Lambda Function
User Proxy : yes
Lambda Function : cspdata-lambda

We also need to setup method ANY and set it to a MOCK. The intention is just to allow POST endpoint and any other endpoint should return 405 Not Allowed.

Create ANY method, with Mock integration type

Update the Method Response and change it to 405 as default. Delete the 200 response and create a new response for 405.

Set the default status to 405

Now go to Integration Response and add a response for 405 status. Delete the 200 response there as well.

Select 405 status in Integration Response as well

After save, expand the newly added 405 response and a mapping template to return empty json along with 405 status.

As a final step, we need to deploy this api. Go to actions and click Deploy API.

Deploy api with these details.

Deployment stage   : [New Stage]
Stage name : latest

Once deployed, you will get this nice api endpoint.

https://e1x3a6m8p4le.execute-api.us-east-1.amazonaws.com/latest

Well… not that nice looking url, but it is good enough to be used for CSP report-uri. We can however setup our own custom url, something like like https://example.com/csp . That can be done using Customs Domain Names, in API Gateway, by assigning a base path to the api. I will leave it out of the scope of this tutorial, to keep things simple.


Testing

Time to tests the csp report api. I am using postman for testing.

GET — Anything except POST call will return 405 Not Allowed
A successful POST will return 204 No Content

Sample violation report source: https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP#Sample_violation_report

Verify the entry in the DynamoDB table.

That’s it. Our API works great and is now ready to be used in CSP report-uri.

We just created our csp report uri through aws console. I will put up a link here for the tutorial on creating the same api using cloudformation very soon. Do let me know if you have any question. Have a great time.

Ganapati Sarkar

Written by

QA, Automation, DevOps and Web Frontend enthusiast with a passion for learning and exploring new technologies.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade