AWS API Gateway Best Practices in-depth
Tags: aws, security, cloud, devops
Published: 2021-06-14 15:30:01

## Best Practices
Forgive me, the [Bart Simpson](https://en.wikipedia.org/wiki/Bart_Simpson) in me couldn't resist using 'Best Practices' once again. Sure, there's a lot to say [to stamp out “best practice”](https://www.satisfice.com/blog/archives/5164) and I agree with most arguments in the article. Framing something as a best practice is subjective, and it could give the impression of being arrogant. But, as grown-ups, I'm convinced most of us know how to deal with the term and see why it's used. So pick the practices you agree on, which you see as 'best' practices yourself.


## A front  door: The importance of API Gateway
I have the feeling that the importance of API Gateway in a setup is sometimes overlooked. AWS wrote down the practices themselves (also using the term 'Best practices :wink:). But IMHO, their [documentation](https://docs.aws.amazon.com/apigateway/latest/developerguide/security-best-practices.html) is a tad too brief. Also, the documentation lacks a 'WHY' in general.

### The 'WHY'
Assuming the vast majority of API Gateways are public-facing, it's easy to picture an API Gateway as a front door. One of the characteristics of a front door is access control: who to let in and how many to let it (at once). 

![Front door](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4o2512zlg1dag8q5tmgq.jpeg)

Nowadays, a front door camera even provides a track record of everyone that came across your door, logging all rejected and allowed entrance calls. 

**That being said, API Gateway is a front door, treat it like one!** It's begging for attention security-wise.

## The Importance of Logs.
People less familiar with security, easily miss the importance of logs. But whoever encountered a security breach will endorse their significance. In case something gets compromised, investigation often starts with looking at access logs. **That is why every public endpoint (Web Server, Load Balancer, API Gateways, ...) should have access logs enabled. If you would ask me, access logs should be mandatory, non-negotiable.**

Knowing this, I find it hard to understand why AWS Security hub only recommends [API logs to be enabled](https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html#apigateway-1-remediation). I have no clue why access logs didn't make it to the [Foundational Security Best Practices standard for CSPM](https://aws.amazon.com/about-aws/whats-new/2021/06/aws-security-hub-adds-16-new-controls-to-its-foundational-securi/) :worried:. I hope AWS will settle this in the next iteration.

### Access Log Retention Period
As a retention period for access logs, I recommend at least one year. To save costs, you could only retain them in CloudWatch for one month. After 30 days, you could transfer them to something like S3. 

If a year sounds long, notice that breaches sometimes stay under the radar for quite some time. To tackle this, ensure your track record is long enough to allow successful investigation in case of trouble.

For the ones using CloudFormation and AWS SAM, here's the IaC:

```YAML
  ApiAccessLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: !Sub "servicename-api-${environment}-ApiGateway-cfn"
      RetentionInDays: 30

  SomeServiceGateway:
    Type: AWS::Serverless::Api
    Properties:
      Name: !Sub "Someservice API GW - ${environment}"
      StageName: "v1"
      EndpointConfiguration: REGIONAL
      TracingEnabled: True
      MethodSettings:
        - LoggingLevel: INFO
          MetricsEnabled: True
          DataTraceEnabled: True
          ResourcePath: "/*"
          HttpMethod: "*"
      AccessLogSetting:
        DestinationArn: !GetAtt ApiAccessLogGroup.Arn
```

## Protecting an Unauthorized API Gateway
Unauthenticated API routes are open to the world. Therefore it's recommended to limit their use. It’s important to protect these unauthenticated API's against common risks, such as denial-of-service attacks or consumer errors.

### AWS WAF
Applying AWS WAF to API Gateway helps to protect an application from SQL injection and cross-site scripting attacks. **It's your first line of defense.**

### AWS CloudFront as traffic absorber
In case of a denial of service attack on an unauthenticated API, it’s possible to exhaust API throttling limits, Lambda concurrency, or DynamoDB provisioned read capacity on an underlying table. Putting an AWS CloudFront distribution in front of the API endpoint with an appropriate time-to-live configuration may help absorb traffic in a DoS attack without changing the underlying solution for fetching the data.

See: "[Operating Lambda: Building a solid security foundation – Part 2](https://aws.amazon.com/blogs/compute/operating-lambda-building-a-solid-security-foundation-part-2/)" for more information.

### Use API keys and Throttling
API Gateway allows throttling when API keys are used. 

Use API Keys for unauthenticated API's when possible and never trust consumers :wink:. Needless to say that negotiated contracts with (API) consumers change over time.  Often these changes aren't briefed. Or maybe a consumer's business just grows, together with the requests he's sending you. But even without any reason, if a consumer sends you a few hundred thousand requests instead of the few hundreds he promised to... who will feel the pain? You or him?

With this in mind, design (your API Gateway) for error. The last thing you want is a drowned service due to a consumer's error. **[Throttling](https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-request-throttling.html) should be enabled by default on your API Gateway. It will prevent you from resource exhaustion, or even worse, scaling to the moon (together with your AWS bill). If a consumer breaks his quota, he should get a `429 Too Many Requests` for coloring outside the lines. Let him feel the pain, not you!**

## Conclusion
Both throttling and logging are easy to enable but can be a real-lifesaver. Forewarned is forearmed.

Enjoy and until next time!