API Gateway Security: Rate Limiting, Auth, and WAF Configuration
A practical guide to securing API gateways: JWT authorizers, API key management, rate limiting tiers, WAF rules, and mutual TLS for service-to-service calls.
API Gateway Security: Rate Limiting, Auth, and WAF Configuration
Your API gateway is the front door to every service in your backend. It is also the primary target for abuse: credential stuffing, enumeration attacks, scraping, and DDoS all arrive through this single chokepoint. Done right, your gateway handles authentication, rate limiting, WAF filtering, and mutual TLS before a single request reaches your application code. Done wrong, it passes everything through and lets your services deal with it — which they rarely do well.
Authentication: Lambda Authorizers and JWT Verification
AWS API Gateway supports three authentication models: IAM auth, Cognito User Pools, and Lambda Authorizers. For most APIs, Lambda Authorizers (or the newer HTTP API JWT Authorizers) are the right choice.
HTTP API JWT Authorizer
For APIs where clients present JWTs (from Auth0, Cognito, or your own IdP), the HTTP API JWT authorizer handles verification without custom code:
# AWS SAM template
MyApi:
Type: AWS::Serverless::HttpApi
Properties:
Auth:
DefaultAuthorizer: MyJWTAuthorizer
Authorizers:
MyJWTAuthorizer:
JwtConfiguration:
issuer: https://your-issuer.auth0.com/
audience:
- https://api.example.com
IdentitySource: "$request.header.Authorization"
API Gateway verifies the JWT signature, expiry, issuer, and audience before the request reaches your Lambda. Invalid tokens get a 401 before any of your code runs.
Lambda Authorizer for Complex Logic
When you need custom logic (API key lookup, permission scopes, tenant isolation), use a Lambda Authorizer:
export const handler = async (event: APIGatewayAuthorizerEvent) => {
const token = event.authorizationToken?.replace('Bearer ', '');
if (!token) {
throw new Error('Unauthorized');
}
const payload = await verifyJWT(token);
return {
principalId: payload.sub,
policyDocument: {
Version: '2012-10-17',
Statement: [{
Action: 'execute-api:Invoke',
Effect: payload.scopes.includes('api:read') ? 'Allow' : 'Deny',
Resource: event.methodArn,
}],
},
context: {
userId: payload.sub,
tenantId: payload.tenant_id,
},
};
};
Enable authorizer caching (5-300 seconds) to avoid verifying the same token on every request — a significant performance and cost improvement under load.
Rate Limiting: Tiers and Burst Control
Flat rate limiting (e.g., 1000 requests/minute for everyone) is too blunt. Real-world APIs need tiered limits that match business requirements: free tier users get less, enterprise customers get more, and abuse patterns are blocked regardless of tier.
AWS API Gateway Usage Plans
# Create a usage plan
aws apigateway create-usage-plan \
--name "free-tier" \
--throttle burstLimit=10,rateLimit=2 \
--quota limit=1000,period=DAY
# Create an API key and associate it
aws apigateway create-api-key --name "user-abc-key" --enabled
aws apigateway create-usage-plan-key \
--usage-plan-id PLAN_ID \
--key-id KEY_ID \
--key-type API_KEY
This enforces a 2 requests/second sustained rate with a burst of 10, and a hard daily cap of 1000 requests. Exceeding limits returns 429 Too Many Requests.
Kong Rate Limiting Plugin
For Kong-based gateways, the rate-limiting plugin supports per-consumer, per-IP, and per-credential policies:
plugins:
- name: rate-limiting
config:
minute: 60
hour: 1000
policy: redis
redis_host: redis.internal
redis_port: 6379
fault_tolerant: false
hide_client_headers: false
Setting policy: redis enables distributed rate limiting across multiple Kong nodes — essential for any horizontally scaled deployment. Without Redis, each node maintains its own counters and the effective rate limit is N × per-node-limit.
WAF Rules: Blocking Common Attack Patterns
A Web Application Firewall at the gateway layer blocks malicious requests before they reach application code. AWS WAF integrates natively with API Gateway.
AWS WAF Managed Rule Groups
resource "aws_wafv2_web_acl" "api_waf" {
name = "api-gateway-waf"
scope = "REGIONAL"
default_action {
allow {}
}
rule {
name = "AWSManagedRulesCommonRuleSet"
priority = 1
override_action { none {} }
statement {
managed_rule_group_statement {
name = "AWSManagedRulesCommonRuleSet"
vendor_name = "AWS"
}
}
visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "CommonRuleSet"
sampled_requests_enabled = true
}
}
rule {
name = "RateLimitRule"
priority = 2
action { block {} }
statement {
rate_based_statement {
limit = 2000
aggregate_key_type = "IP"
}
}
visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "RateLimit"
sampled_requests_enabled = true
}
}
}
The AWSManagedRulesCommonRuleSet blocks OWASP Top 10 patterns including SQLi, XSS, and path traversal. The rate-based rule adds a per-IP circuit breaker independent of usage plans.
Custom Rules for API-Specific Patterns
WAF managed rules are good for known attack signatures, but your API may need custom rules. For example, blocking requests with anomalous Content-Type headers for your JSON API:
# Block requests that claim to be JSON but have suspicious content
Rule: body contains 'UNION SELECT' or body contains '<script>'
Mutual TLS for Service-to-Service APIs
For internal APIs (service mesh bypass, partner integrations, machine-to-machine), mutual TLS (mTLS) ensures both the client and server verify each other's identity. API Gateway HTTP APIs support mTLS natively.
Configuring mTLS on API Gateway
# Upload your CA certificate bundle to S3
aws s3 cp ca-bundle.pem s3://your-truststore-bucket/ca-bundle.pem
# Enable mTLS on the API custom domain
aws apigatewayv2 update-domain-name \
--domain-name api.internal.example.com \
--domain-name-configurations \
CertificateArn=arn:aws:acm:us-east-1:123456789:certificate/abc,\
EndpointType=REGIONAL \
--mutual-tls-authentication \
TruststoreUri=s3://your-truststore-bucket/ca-bundle.pem
With mTLS enabled, clients must present a certificate signed by your CA. Requests without a valid client certificate are rejected at the TLS handshake layer — before any HTTP processing.
Request Validation
API Gateway can validate request bodies and parameters against a model schema, rejecting malformed requests before they reach your Lambda:
{
"type": "object",
"required": ["email", "amount"],
"properties": {
"email": {
"type": "string",
"format": "email"
},
"amount": {
"type": "number",
"minimum": 0.01,
"maximum": 10000
}
},
"additionalProperties": false
}
Enabling additionalProperties: false prevents parameter pollution attacks where extra fields might be interpreted in unexpected ways by downstream services.
Logging and Observability
All of this is only useful if you can see what is happening. Enable access logging to CloudWatch Logs with a structured format:
$context.requestId $context.identity.sourceIp $context.requestTime $context.httpMethod $context.path $context.status $context.responseLength $context.authorizer.principalId
Feed these logs into your SIEM and create alerts for:
- 401/403 rate spikes (credential stuffing or auth bypass attempts)
- 429 rate limit hits from a single IP
- WAF block counts increasing
- Unusual request patterns (scanning, enumeration)
A well-configured API gateway does not just route traffic — it is your first and most consistent security control for every API endpoint you expose.