Front-End Web & Mobile

AppSync adds support for AWS WAF

This article was written by Brice Pellé, Principal Specialist Solutions Architect, AWS

 

AWS AppSync is a fully managed service that allows you to deploy Serverless GraphQL backends in the AWS cloud. With AppSync, you can have GraphQL backends that connect to multiple data sources, and that scale and grow with your usage. Whether you are just getting started, or scaling out your APIs, protecting your endpoints to maintain security and high availability is always important and top priority.

Today, we are announcing AWS AppSync integration with AWS WAF. AWS WAF is a web application firewall that helps protect APIs like AppSync GraphQL endpoints against common web exploits that may affect availability, compromise security, or consume excessive resources. AWS WAF allows you to quickly get started by using Managed Rules for AWS WAF that are automatically updated as new issues emerge. Additionally, you can create your own rules such as regular rules that block requests based on their origin and content, and rate-based rules that limit the number of requests per IP address over a span of time.

In this article, we show how to get started using an AWS WAF Web Access Control List (Web ACL) to protect an AppSync API by implementing a rate-based rule to throttle requests. We then show how to use a regular rule to disable GraphQL introspection in case introspection shouldn’t be available to clients due to specific organizational requirements. You can use this walkthrough with any AppSync API. We use the sample Event App API available in the AppSync Console wizard to demonstrate the AWS WAF integration. You can deploy this API from the AppSync console by clicking Create API and selecting Event App in the Start from a sample project section.

 

Protect your API by throttling requests with a rate-based rule

You are going to create a new web ACL with a rate-based rule that tracks the rate of requests for each originating IP address. The rule triggers an action on IPs with rates that go over a defined limit over a 5-minute time span. When the rule is triggered, AWS WAF applies the action to additional requests from the same IP address until the request rate falls below the limit.

Follow these steps to create a web ACL:

  1. Open the AWS WAF Console.
  2. Choose Create web ACL.
  3. For Name, enter AppSync-First-ACL.
  4. For Resource type, choose Regional resources
  5. For Region, choose the same region where your AppSync API is deployed.
  6. Click Next. You do not need to associate an AWS resource at this time, while you can click on Add AWS Resources and select your AppSync API we show how to do it from the AppSync console in the following steps.
  7. In the next step on Add rules and rule groups, click Add rules and Add my own rules and rule groups
  8. For Name, enter Throttling-Rule.
  9. For Rule type, choose Rate-based rule.
  10. For Rate limit, enter 200.
  11. For IP address to use for rate limiting, select IP address in header
  12. Click Add rule.
  13. Click Next until you get to Step 5: Review and create web ACL, then confirm your options and click Create web ACL.

Once it is done, head to the AWS AppSync Console in the same region. Select your API. Under settings, toggle Enable AWS WAF on and select your ACL in the Web ACL dropdown. Click Save to associate the ACL with your API.

 

Testing your AppSync API now secured by AWS WAF

You’ve set up the Web ACL rate-based rule to trigger after 200 requests have been received in a 5-minute span from a specific source IP address. After this threshold is breached, additional requests from the same address are blocked until the request rate falls below the threshold.

You can make a simple request to your API from a terminal with curl, inserting your own API URL and API Key retrieved from the Settings section of your AppSync API in the console.

$ export APPSYNC_API_URL=<APPSYNC API URL> # insert your own API URL
$ export APPSYNC_API_KEY=<APPSYNC API KEY> # insert your own API KEY
$ curl -XPOST $APPSYNC_API_URL -H "Content-Type:application/graphql" -H "x-api-key:$APPSYNC_API_KEY" -d '{"query": "query ListEvents { listEvents { items { id } } }"}'

This returns a list of Events. For example:

{
	"data": {
		"listEvents": {
			"items": []
		}
	}
}

The list is empty because we haven’t created any events by executing a mutation, however the command shows API calls are successful. You can use Artillery, an open-source and easy-to-use performance testing toolkit, to implement a simple load test.

To use Artillery:

1. Install the CLI tool with npm:

$ npm install -g artillery

2. Define your load test. For example, create a file test.yaml that creates 10 requests per second over 2 minutes (1200 requests). Note that the script references the 2 environment variables defined above.

config:
  target: "{{ $processEnvironment.APPSYNC_API_URL }}"
  phases:
    - duration: 120
      arrivalRate: 10
  defaults:
    headers:
      x-api-key: "{{ $processEnvironment.APPSYNC_API_KEY }}"
      Content-Type: 'application/graphql'
scenarios:
  - flow:
      - post:
          url: '/'
          json:
            query: 'query ListEvents { listEvents { items { id } } }'

3. Run the test:

$ artillery run test.yaml

4. The test runs and returns a summary. The 403 HTTP responses indicate the number of requests that were denied after the rule action was triggered.

Elapsed time: 1 minute, 10 seconds
  Scenarios launched:  100
  Scenarios completed: 100
  Requests completed:  100
  Mean response/sec: 10.02
  Response time (msec):
    min: 142.2
    max: 145.8
    median: 144
    p95: 145.8
    p99: 145.8
  Codes:
    200: 56
    403: 44

Run the curl command one more time:

curl -XPOST $APPSYNC_API_URL -H "Content-Type:application/graphql" -H "x-api-key:$APPSYNC_API_KEY" -d '{"query": "query ListEvents { listEvents { items { id } } }"}'

You see the response showing that WAF has blocked the request. WAF applies the action to additional requests from the IP address until the request rate falls below the limit:

{
  "errors" : [ {
    "errorType" : "WAFForbiddenException",
    "message" : "Forbidden"
  } ]
}

 

Disabling introspection queries for specific API Keys

You may have a use case where you provide public access to an API without specific authentication requirements, and using an API key. For security reasons, you want to disable introspection queries on your API as there might be a requirement to prevent clients to view the whole schema. You can create a new rule to block these requests. Introspection queries are made by using the special __schema field. For example:

{
  __schema {
    types {
      name
    }
  }
}

First create a new API key for your AppSync API in the AppSync console under Settings.

Follow these steps to create a new rule in your ACL:

  1. In the WAF console, click on AppSync-First-ACL, select the Rules tab, then click Add rules and Add my own rules and rule groups.
  2. For Name, enter Introspection.
  3. For Rule type, choose Regular rule.
  4. For If a request, select matches all the statements (AND)
  5. For Statement 1:
    • Inspect: Header
    • Header field name: x-api-key
    • Match type: Exactly matches string
    • String to match: <paste the API KEY you want to block>
    • Text transformation: Lowercase
  6. Add another statement. For Statement 2:
    1. Inspect: Body
    2. Match type: Contains string
    3. String to match: __schema
  7. Click Add rule and Save.

 

Run the curl command to execute an introspection query with the default key:

$ export APPSYNC_API_KEY=<APPSYNC API KEY> # insert your default API KEY
$ curl -XPOST $APPSYNC_API_URL -H "Content-Type:application/graphql" -H "x-api-key:$APPSYNC_API_KEY" -d '{"query": " { __schema{ types { name } } }"}'

You should see the introspection results. Now run the command with the API key you want to block:

$ export APPSYNC_API_KEY=<APPSYNC API KEY> # insert your second API KEY
$ curl -XPOST $APPSYNC_API_URL -H "Content-Type:application/graphql" -H "x-api-key:$APPSYNC_API_KEY" -d '{"query": " { __schema{ types { name } } }"}'

You see the following results showing that the request was denied by WAF:

{
  "errors" : [ {
    "errorType" : "WAFForbiddenException",
    "message" : "Forbidden"
  } ]
}

Conclusion

It is now easier than ever to protect your AppSync API by using the power of AWS WAF. AWS WAF provides resources such as managed rules that are now directly available to your AppSync API. You can also deploy the AWS WAF Security Automations to quickly create custom, application-specific rules that block common attack patterns.

You can use AWS WAF with AWS AppSync today in any region where both AppSync and WAF are available. For more information, refer to the AppSync documentation and the WAF documentation.