
π‘οΈ Ultimate Guide: JWT-Based Rate Limiting with AWS WAF π
This comprehensive guide explains how to implement rate limiting based on JWT (JSON Web Token) authorization values using AWS WAF and Lambda@Edge. The article addresses a common security challenge: controlling request rates for specific users or tokens in web applications.
π Master JWT Rate Limiting: A Developer's Guide to AWS WAF Security!
π Why Should You Care?
Protect your Application/APIs from abuse and ensure fair usage by implementing smart rate limiting based on JWT tokens. This guide shows you how!
π― What You'll Need
β AWS account with proper permissions
β Basic knowledge of Lambda, CloudFront, and JWT
β Your favorite code editor
π‘ JWT Token Structure
Sample JWT token:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsInRva2VuIjoiYWJjZGVmZzEyMyJ9 Decoding the above will result the below output: { "alg": "HS256", "typ": "JWT", "token": "abcdefg123" }
Note: Token may contain multiple fields including the timestamp and singed signature. If the JWT token is constant and change only one value for each user then using Text-transform of Base64 extension and create a rate-based rule using custom key (ie., Text transform: Base64 decode extension) would the option.
π οΈ Super Quick Setup

π Implementation Steps
1οΈβ£ IAM Setup
- π€ Create IAM Role with Trust Relationship
2οΈβ£ Lambda Function Setup
- π Name: Any name as per your reference
- βοΈ Runtime: Python 3.9
- π» Architecture: x86_64
- Add Trust Relationship IAM role
3οΈβ£ Lambda Magic β¨
- Create a code to decode JWT header and Add a X-Token header when forwarding to Origin.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
import json
import base64
from typing import Dict, Any
# Constants
HEADER_NAME = 'X-Token' # Header name for the token value
def decode_jwt_header(token: str) -> Dict[str, Any]:
"""
Decode the JWT header and extract token information.
Args:
token (str): JWT token string
Returns:
Dict[str, Any]: Decoded JWT header data
Raises:
Exception: If token decoding fails
"""
try:
# Remove 'Bearer ' prefix if present
if token.startswith('Bearer '):
token = token.split(' ')[1]
# Extract and decode header segment
header_segment = token.split('.')[0]
padding = '=' * (4 - (len(header_segment) % 4))
decoded_header = base64.urlsafe_b64decode(header_segment + padding)
return json.loads(decoded_header)
except Exception as e:
raise Exception(f"Error decoding JWT header: {str(e)}")
def lambda_handler(event: Dict[str, Any], context: Any) -> Dict[str, Any]:
"""
AWS Lambda handler to process CloudFront requests and add X-Token header.
Args:
event (Dict[str, Any]): Lambda event object
context (Any): Lambda context object
Returns:
Dict[str, Any]: Modified request object
"""
try:
request = event['Records'][0]['cf']['request']
headers = request['headers']
# Return unmodified request if no authorization header
if 'authorization' not in headers:
return request
# Process authorization header
auth_header = headers['authorization'][0]['value']
decoded_header = decode_jwt_header(auth_header)
# Add X-Token header if token exists in decoded header
if 'token' in decoded_header:
request['headers'][HEADER_NAME.lower()] = [{
'key': HEADER_NAME,
'value': str(decoded_header['token'])
}]
print(f"Successfully added {HEADER_NAME}: {decoded_header['token']}")
# Clean up headers by removing CloudFront-Viewer details
headers_to_remove = [
header for header in headers
if header.startswith('cloudfront-viewer-')
]
for header in headers_to_remove:
headers.pop(header, None)
return request
except Exception as e:
print(f"Error processing request: {str(e)}")
return event['Records'][0]['cf']['request'] # Return original request on error
4οΈβ£ CloudFront Configuration
- Create a origin and behavior
- Add Lambda Function Association to the behavior.
- Event Type: Origin Request
5οΈβ£ WAF Rule Configuration
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
{
"Name": "JWT-Rate-Based",
"Priority": 0,
"Statement": {
"RateBasedStatement": {
"Limit": 10,
"EvaluationWindowSec": 300,
"AggregateKeyType": "CUSTOM_KEYS",
"CustomKeys": [
{
"Header": {
"Name": "X-Token",
"TextTransformations": [
{
"Priority": 0,
"Type": "NONE"
}
]
}
}
]
}
},
"Action": {
"Block": {}
},
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "JWT-Rate-Based"
}
}π§ͺ Test It Out
:curl -H "Authorization: Bearer JWT_TOKEN" https://[your-cloudfront]/pathπ‘ Pro Tip
For constant JWT tokens with single varying value, use Base64 decode text transformation in your rate-based rule! (ie., Text transform: Base64 decode extension).
Enjoyed reading this content? Let the author know!
Your likes, comments, shares, and saves help creators reach more builders.
Loading recommendations
Loading article