Serverless Framework: Securing AWS Lambda Endpoints

The Serverless Framework makes coding, deploying, and maintaining serverless functions a breeze. That's a great set of characteristics, but what about security? Well, securing API endpoints is straightforward, too.

This post shows you how to secure your AWS Lambda endpoints in three steps.

  1. Add apiKey property to serverless.yml
  2. Copy the key that is returned after deployment
  3. Use the key as your x-api-key header in your API requests

The apiKey Property

Generating an api key is as simple as adding an additional property to your serverless.yml file. Under provider, add the apiKey property and list the names of api keys you want generated (lines 7-8 below).

provider:  
  name: aws
  runtime: python3.6 # This could be any runtime
  stage: dev
  region: us-west-1
  apiKeys:
    - celery_${opt:stage}
    - anotherKey_${opt:stage}

Notice the ${opt:stage} part of the keys. This syntax is how you use variables in serverless.yml. It takes the stage and inserts it where the variable syntax is placed. When you deploy to dev, the celery key is celery_dev. When you deploy to production the key is celery_production.

This isn't a requirement, but it's a good idea to include if you're deploying to multiple environments. A key name can only be used once in a project, so if your dev deployment uses the key name celery, then celery can't be used for your production deployment. You'll get an error saying the key name is already in use. Using the syntax above (lines 7-8) circumvents these naming issues.

Additionally, you can assign different requesters different keys (e.g. some use the celery key, others use the anotherKey key) if you want to keep track of where your requests are coming from.

Retrieving the apiKey Value

After deployment, the following information is returned:

Serverless: Stack update finished...  
Service Information  
service: my-lambda-service  
stage: dev  
region: us-west-1  
stack: update-account-columns-dev  
apiKeys:  
  celery_qa: KrdJFKaodifhoae76asdfkj987dfliIdaffiD
  anotherKey_qa: dsAfJaodifhoae76asdfkj987dfliiuehfns6
endpoints:  
  POST - https://dkfj7dkfh7.execute-api.us-west-1.amazonaws.com/dev/
functions:  
  my_lambda: my-lambda-service-dev-my_lambda

If you've already deployed, and need this information again, run sls info -s <dev/qa/stage/production/whatevs> from the root of your serverless project. For example, to get the info above, I could run sls info -s dev.

Lines 7-9 above show two api keys, and their values. You'll need to use one of these values to get through to your endpoint.

Using the x-api-key Header

Using the API key is no more difficult than adding a header to your request. Specifically, you need to add an x-api-key header.

Below you'll find two examples. The first in Python using the requests package. The second in Node.js using the new r2 package (which is Mikeal Rogers's – the creator of request – latest http library)

Python using requests.

import requests

headers = {  
    "x-api-key": "KrdJFKaodifhoae76asdfkj987dfliIdaffiD"
}

my_profile = requests.get(  
    "https://your-api/dev", headers=headers)

print(my_profile.content)  

Node using r2.

const r2 = require("r2");

const headers = {  
    "x-api-key": "dsAfJaodifhoae76asdfkj987dfliiuehfns6" 
};

const myProfile = () => {  
  return r2.get("https://your-api/dev", {
    headers
  }).response;
};

myProfile()  
  .then(res => res.json())
  .then(json => console.log(json));

Conclusion

API keys are not a complete security solution, but are a great start to securing your endpoints. And, by all means, using API keys to secure your endpoints is much better than no security at all!

For more on security, check out this great overview of different security strategies, including a discussion of use-cases for API keys.