npx skills add mukul975/Anthropic-Cybersecurity-SkillsMITRE ATT&CK
When to Use
- When deploying API Gateway endpoints that require protection against common web attacks
- When implementing rate limiting and throttling to prevent API abuse and DDoS attacks
- When building bot detection and mitigation for API endpoints exposed to the internet
- When compliance requires WAF protection for all public-facing API endpoints
- When customizing access controls based on IP reputation, geolocation, or request patterns
Do not use for network-level DDoS protection (use AWS Shield), for application logic vulnerabilities (use SAST/DAST tools), or for internal API security between microservices (use service mesh authentication and authorization).
Prerequisites
- AWS API Gateway (REST or HTTP API) deployed with public endpoints
- IAM permissions for
wafv2:*andapigateway:*operations - CloudWatch and S3 or Kinesis Firehose configured for WAF logging
- Understanding of the API's expected traffic patterns for rate limiting configuration
- IP reputation lists or threat intelligence feeds for custom IP blocking
Workflow
Step 1: Create a WAF Web ACL with Managed Rule Groups
Create a Web ACL with AWS Managed Rules for baseline protection against OWASP Top 10 attacks.
# Create a WAF Web ACL with managed rule groups
aws wafv2 create-web-acl \
--name api-gateway-waf \
--scope REGIONAL \
--default-action '{"Allow":{}}' \
--visibility-config '{
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "api-gateway-waf"
}' \
--rules '[
{
"Name": "AWSManagedRulesCommonRuleSet",
"Priority": 1,
"Statement": {
"ManagedRuleGroupStatement": {
"VendorName": "AWS",
"Name": "AWSManagedRulesCommonRuleSet"
}
},
"OverrideAction": {"None": {}},
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "CommonRuleSet"
}
},
{
"Name": "AWSManagedRulesKnownBadInputsRuleSet",
"Priority": 2,
"Statement": {
"ManagedRuleGroupStatement": {
"VendorName": "AWS",
"Name": "AWSManagedRulesKnownBadInputsRuleSet"
}
},
"OverrideAction": {"None": {}},
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "KnownBadInputs"
}
},
{
"Name": "AWSManagedRulesSQLiRuleSet",
"Priority": 3,
"Statement": {
"ManagedRuleGroupStatement": {
"VendorName": "AWS",
"Name": "AWSManagedRulesSQLiRuleSet"
}
},
"OverrideAction": {"None": {}},
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "SQLiRuleSet"
}
},
{
"Name": "AWSManagedRulesAmazonIpReputationList",
"Priority": 4,
"Statement": {
"ManagedRuleGroupStatement": {
"VendorName": "AWS",
"Name": "AWSManagedRulesAmazonIpReputationList"
}
},
"OverrideAction": {"None": {}},
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "IPReputationList"
}
}
]'Step 2: Add Rate Limiting Rules
Configure rate-based rules to throttle excessive API requests per IP address.
# Get the Web ACL ARN and lock token
WEB_ACL_ARN=$(aws wafv2 list-web-acls --scope REGIONAL \
--query "WebACLs[?Name=='api-gateway-waf'].ARN" --output text)
# Update Web ACL to add rate limiting rule
aws wafv2 update-web-acl \
--name api-gateway-waf \
--scope REGIONAL \
--id $(aws wafv2 list-web-acls --scope REGIONAL --query "WebACLs[?Name=='api-gateway-waf'].Id" --output text) \
--lock-token $(aws wafv2 get-web-acl --name api-gateway-waf --scope REGIONAL --id WEB_ACL_ID --query 'LockToken' --output text) \
--default-action '{"Allow":{}}' \
--visibility-config '{
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "api-gateway-waf"
}' \
--rules '[
{
"Name": "RateLimitPerIP",
"Priority": 0,
"Statement": {
"RateBasedStatement": {
"Limit": 2000,
"AggregateKeyType": "IP"
}
},
"Action": {"Block": {}},
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "RateLimitPerIP"
}
},
{
"Name": "RateLimitLoginEndpoint",
"Priority": 5,
"Statement": {
"RateBasedStatement": {
"Limit": 100,
"AggregateKeyType": "IP",
"ScopeDownStatement": {
"ByteMatchStatement": {
"FieldToMatch": {"UriPath": {}},
"PositionalConstraint": "STARTS_WITH",
"SearchString": "/api/auth/login",
"TextTransformations": [{"Priority": 0, "Type": "LOWERCASE"}]
}
}
}
},
"Action": {"Block": {}},
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "RateLimitLogin"
}
}
]'Step 3: Implement Bot Control
Add AWS WAF Bot Control to detect and manage automated traffic.
# Add Bot Control managed rule group
# (Add to the rules array when updating the Web ACL)
{
"Name": "AWSManagedRulesBotControlRuleSet",
"Priority": 6,
"Statement": {
"ManagedRuleGroupStatement": {
"VendorName": "AWS",
"Name": "AWSManagedRulesBotControlRuleSet",
"ManagedRuleGroupConfigs": [{
"AWSManagedRulesBotControlRuleSet": {
"InspectionLevel": "COMMON"
}
}],
"ExcludedRules": [
{"Name": "CategoryHttpLibrary"},
{"Name": "SignalNonBrowserUserAgent"}
]
}
},
"OverrideAction": {"None": {}},
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "BotControl"
}
}Step 4: Create Custom Rules for API-Specific Protection
Build custom WAF rules for API-specific security requirements.
# Block requests without required API key header
{
"Name": "RequireAPIKey",
"Priority": 7,
"Statement": {
"NotStatement": {
"Statement": {
"ByteMatchStatement": {
"FieldToMatch": {
"SingleHeader": {"Name": "x-api-key"}
},
"PositionalConstraint": "EXACTLY",
"SearchString": "",
"TextTransformations": [{"Priority": 0, "Type": "NONE"}]
}
}
}
},
"Action": {"Block": {"CustomResponse": {"ResponseCode": 403}}},
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "RequireAPIKey"
}
}
# Geo-restrict to allowed countries
{
"Name": "GeoRestriction",
"Priority": 8,
"Statement": {
"NotStatement": {
"Statement": {
"GeoMatchStatement": {
"CountryCodes": ["US", "CA", "GB", "DE", "FR", "AU"]
}
}
}
},
"Action": {"Block": {}},
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "GeoRestriction"
}
}
# Block oversized request bodies (prevent payload attacks)
{
"Name": "MaxBodySize",
"Priority": 9,
"Statement": {
"SizeConstraintStatement": {
"FieldToMatch": {"Body": {"OversizeHandling": "MATCH"}},
"ComparisonOperator": "GT",
"Size": 10240,
"TextTransformations": [{"Priority": 0, "Type": "NONE"}]
}
},
"Action": {"Block": {}},
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "MaxBodySize"
}
}Step 5: Associate WAF with API Gateway and Enable Logging
Attach the Web ACL to the API Gateway stage and configure comprehensive logging.
# Associate Web ACL with API Gateway
aws wafv2 associate-web-acl \
--web-acl-arn $WEB_ACL_ARN \
--resource-arn arn:aws:apigateway:us-east-1::/restapis/API_ID/stages/prod
# Enable WAF logging to S3 via Kinesis Firehose
aws wafv2 put-logging-configuration \
--logging-configuration '{
"ResourceArn": "'$WEB_ACL_ARN'",
"LogDestinationConfigs": [
"arn:aws:firehose:us-east-1:ACCOUNT:deliverystream/aws-waf-logs-api-gateway"
],
"RedactedFields": [
{"SingleHeader": {"Name": "authorization"}},
{"SingleHeader": {"Name": "cookie"}}
]
}'
# Verify association
aws wafv2 get-web-acl-for-resource \
--resource-arn arn:aws:apigateway:us-east-1::/restapis/API_ID/stages/prodStep 6: Monitor WAF Metrics and Tune Rules
Monitor WAF effectiveness and tune rules to reduce false positives.
# Get WAF metrics from CloudWatch
aws cloudwatch get-metric-statistics \
--namespace AWS/WAFV2 \
--metric-name BlockedRequests \
--dimensions Name=WebACL,Value=api-gateway-waf Name=Rule,Value=ALL \
--start-time 2026-02-22T00:00:00Z \
--end-time 2026-02-23T00:00:00Z \
--period 3600 \
--statistics Sum
# Get sampled requests for a specific rule
aws wafv2 get-sampled-requests \
--web-acl-arn $WEB_ACL_ARN \
--rule-metric-name RateLimitPerIP \
--scope REGIONAL \
--time-window '{"StartTime":"2026-02-22T00:00:00Z","EndTime":"2026-02-23T00:00:00Z"}' \
--max-items 50
# Check rate-limited IPs
aws wafv2 get-rate-based-statement-managed-keys \
--web-acl-name api-gateway-waf \
--scope REGIONAL \
--web-acl-id WEB_ACL_ID \
--rule-name RateLimitPerIP
# Create CloudWatch alarm for high block rate
aws cloudwatch put-metric-alarm \
--alarm-name waf-high-block-rate \
--namespace AWS/WAFV2 \
--metric-name BlockedRequests \
--dimensions Name=WebACL,Value=api-gateway-waf Name=Rule,Value=ALL \
--statistic Sum --period 300 --threshold 1000 \
--comparison-operator GreaterThanThreshold \
--evaluation-periods 2 \
--alarm-actions arn:aws:sns:us-east-1:ACCOUNT:security-alertsKey Concepts
| Term | Definition |
|---|---|
| Web ACL | AWS WAF access control list that defines the collection of rules and their actions (allow, block, count) applied to associated resources |
| Managed Rule Group | Pre-configured set of WAF rules maintained by AWS or third-party vendors for common attack patterns like OWASP Top 10 |
| Rate-Based Rule | WAF rule that tracks request rates per IP address and blocks traffic exceeding a defined threshold within a 5-minute window |
| Bot Control | AWS WAF managed rule group that identifies and manages automated traffic including scrapers, crawlers, and attack bots |
| IP Reputation List | AWS-maintained list of IP addresses associated with malicious activity including botnets, scanners, and known attackers |
| Custom Response | WAF capability to return specific HTTP status codes and custom response bodies when blocking requests |
Tools & Systems
- AWS WAF: Web application firewall service for protecting API Gateway, ALB, CloudFront, and AppSync endpoints
- AWS Managed Rules: Pre-built rule groups for common attack patterns maintained by AWS security team
- AWS Firewall Manager: Central management of WAF policies across multiple accounts in AWS Organizations
- Kinesis Firehose: Streaming delivery service for WAF logs to S3, Elasticsearch, or third-party analytics
- CloudWatch: Monitoring service for WAF metrics including allowed, blocked, and counted requests
Common Scenarios
Scenario: Protecting a Public API from Credential Stuffing and Bot Attacks
Context: A public REST API experiences thousands of authentication attempts per hour from automated bots attempting credential stuffing against the /api/auth/login endpoint.
Approach:
- Create a Web ACL with AWS Managed Rules Common Rule Set for baseline protection
- Add a rate-based rule limiting the login endpoint to 100 requests per IP per 5 minutes
- Enable Bot Control managed rules to detect and block automated traffic
- Add IP Reputation List to block known malicious IPs proactively
- Create a custom rule blocking requests without proper User-Agent headers
- Enable WAF logging and create CloudWatch alarms for high block rates
- Review sampled blocked requests weekly to tune rules and reduce false positives
Pitfalls: Rate limiting by IP can block legitimate users behind shared NAT gateways or corporate proxies. Consider using API key or authenticated session-based rate limiting for more granular control. Bot Control rules in COMMON inspection level may block legitimate API clients; start in Count mode and review before switching to Block.
Output Format
AWS WAF API Gateway Security Report
======================================
Web ACL: api-gateway-waf
Associated Resource: API Gateway - production-api (prod stage)
Report Period: 2026-02-16 to 2026-02-23
TRAFFIC SUMMARY:
Total requests: 2,450,000
Allowed requests: 2,380,000 (97.1%)
Blocked requests: 70,000 (2.9%)
BLOCKS BY RULE:
RateLimitPerIP: 28,000 (40%)
AWSManagedRulesCommonRuleSet: 18,000 (25.7%)
BotControl: 12,000 (17.1%)
SQLiRuleSet: 5,000 (7.1%)
IPReputationList: 4,000 (5.7%)
RateLimitLogin: 2,000 (2.9%)
GeoRestriction: 1,000 (1.4%)
TOP BLOCKED IPs:
185.x.x.x: 8,400 requests (rate limited)
45.x.x.x: 5,200 requests (bot detected)
198.x.x.x: 3,100 requests (SQLi attempts)
ATTACK TYPES BLOCKED:
Credential stuffing (login endpoint): 2,000
SQL injection attempts: 5,000
Cross-site scripting: 3,200
Known bad bot traffic: 12,000
Rate limit violations: 28,000
WAF RULE HEALTH:
Rules in Block mode: 8 / 10
Rules in Count mode: 2 / 10 (under evaluation)
False positive rate: < 0.1%References and resources
Everything below is rendered for inspection. Script files are read-only and never run.
References 1
api-reference.md2.3 KB
API Reference: Securing API Gateway with AWS WAF
boto3 WAFv2 Client
Installation
pip install boto3Client Initialization
client = boto3.client("wafv2", region_name="us-east-1")Key Methods
| Method | Description |
|---|---|
create_web_acl() |
Create a new Web ACL with rules and default action |
update_web_acl() |
Modify rules, requires LockToken for optimistic concurrency |
get_web_acl() |
Retrieve full Web ACL configuration and lock token |
list_web_acls() |
List all Web ACLs in a scope (REGIONAL or CLOUDFRONT) |
associate_web_acl() |
Attach Web ACL to API Gateway, ALB, or AppSync |
get_sampled_requests() |
Retrieve sampled requests for a specific rule metric |
get_rate_based_statement_managed_keys() |
Get IPs currently rate-limited |
put_logging_configuration() |
Configure WAF logging to Firehose/S3 |
list_resources_for_web_acl() |
List resources associated with a Web ACL |
Managed Rule Groups
| Rule Group | Protection |
|---|---|
AWSManagedRulesCommonRuleSet |
OWASP Top 10 common attacks |
AWSManagedRulesSQLiRuleSet |
SQL injection patterns |
AWSManagedRulesKnownBadInputsRuleSet |
Known bad request patterns |
AWSManagedRulesAmazonIpReputationList |
Malicious IP blocking |
AWSManagedRulesBotControlRuleSet |
Bot detection and management |
Rate-Based Rule Parameters
| Parameter | Type | Description |
|---|---|---|
Limit |
int | Max requests per 5-minute window (min: 100) |
AggregateKeyType |
str | IP or FORWARDED_IP |
ScopeDownStatement |
dict | Optional filter to scope rate limiting |
CloudWatch Metrics (Namespace: AWS/WAFV2)
| Metric | Description |
|---|---|
AllowedRequests |
Requests allowed by WAF |
BlockedRequests |
Requests blocked by WAF |
CountedRequests |
Requests matched in Count mode |
PassedRequests |
Requests not matching any rule |
References
- boto3 WAFv2 docs: https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/wafv2.html
- AWS WAF Developer Guide: https://docs.aws.amazon.com/waf/latest/developerguide/
- AWS Managed Rules list: https://docs.aws.amazon.com/waf/latest/developerguide/aws-managed-rule-groups-list.html
Scripts 1
agent.py7.0 KB
#!/usr/bin/env python3
"""Agent for managing AWS WAF Web ACLs protecting API Gateway endpoints."""
import boto3
import json
import argparse
from datetime import datetime, timedelta, timezone
def get_waf_client(region="us-east-1"):
"""Create WAFv2 client for regional resources."""
return boto3.client("wafv2", region_name=region)
def list_web_acls(client, scope="REGIONAL"):
"""List all Web ACLs in the account."""
response = client.list_web_acls(Scope=scope)
acls = response.get("WebACLs", [])
print(f"[*] Found {len(acls)} Web ACL(s)")
for acl in acls:
print(f" - {acl['Name']} (ID: {acl['Id']})")
return acls
def get_web_acl_details(client, name, acl_id, scope="REGIONAL"):
"""Get detailed Web ACL configuration including all rules."""
response = client.get_web_acl(Name=name, Scope=scope, Id=acl_id)
acl = response["WebACL"]
lock_token = response["LockToken"]
print(f"\n[*] Web ACL: {acl['Name']}")
print(f" ARN: {acl['ARN']}")
print(f" Default Action: {list(acl['DefaultAction'].keys())[0]}")
print(f" Rules: {len(acl.get('Rules', []))}")
for rule in acl.get("Rules", []):
action = list(rule.get("Action", rule.get("OverrideAction", {})).keys())[0]
print(f" [{rule['Priority']}] {rule['Name']} -> {action}")
return acl, lock_token
def check_waf_association(client, web_acl_arn):
"""Check which resources are associated with a Web ACL."""
resource_types = ["API_GATEWAY", "APPLICATION_LOAD_BALANCER", "APPSYNC", "COGNITO_USER_POOL"]
associations = []
for rt in resource_types:
try:
resp = client.list_resources_for_web_acl(WebACLArn=web_acl_arn, ResourceType=rt)
for arn in resp.get("ResourceArns", []):
associations.append({"type": rt, "arn": arn})
print(f" [+] Associated: {rt} -> {arn}")
except client.exceptions.WAFInvalidParameterException:
continue
if not associations:
print(" [-] No resources associated with this Web ACL")
return associations
def get_sampled_requests(client, web_acl_arn, rule_metric, scope="REGIONAL", minutes=60):
"""Get sampled requests for a specific rule to analyze blocks."""
end_time = datetime.now(timezone.utc)
start_time = end_time - timedelta(minutes=minutes)
try:
response = client.get_sampled_requests(
WebAclArn=web_acl_arn, RuleMetricName=rule_metric, Scope=scope,
TimeWindow={"StartTime": start_time, "EndTime": end_time}, MaxItems=50,
)
samples = response.get("SampledRequests", [])
print(f"\n[*] Sampled requests for rule '{rule_metric}': {len(samples)}")
for s in samples[:10]:
req = s["Request"]
print(f" [{s.get('Action', '?')}] {req['Method']} {req.get('URI', '/')} "
f"from {req.get('ClientIP', '?')} at {s.get('Timestamp', '?')}")
return samples
except Exception as e:
print(f" [-] Error getting samples: {e}")
return []
def get_rate_based_keys(client, name, acl_id, rule_name, scope="REGIONAL"):
"""Get IPs currently rate-limited by a rate-based rule."""
try:
response = client.get_rate_based_statement_managed_keys(
Scope=scope, WebACLName=name, WebACLId=acl_id, RuleName=rule_name,
)
keys = response.get("ManagedKeysIPV4", {}).get("Addresses", [])
keys += response.get("ManagedKeysIPV6", {}).get("Addresses", [])
print(f"\n[*] Rate-limited IPs for rule '{rule_name}': {len(keys)}")
for ip in keys:
print(f" [!] {ip}")
return keys
except Exception as e:
print(f" [-] Error: {e}")
return []
def get_waf_metrics(region="us-east-1", acl_name="api-gateway-waf", hours=24):
"""Get WAF CloudWatch metrics for blocked and allowed requests."""
cw = boto3.client("cloudwatch", region_name=region)
end_time = datetime.now(timezone.utc)
start_time = end_time - timedelta(hours=hours)
metrics = {}
for metric_name in ["AllowedRequests", "BlockedRequests"]:
response = cw.get_metric_statistics(
Namespace="AWS/WAFV2", MetricName=metric_name,
Dimensions=[{"Name": "WebACL", "Value": acl_name}, {"Name": "Rule", "Value": "ALL"}],
StartTime=start_time, EndTime=end_time, Period=3600, Statistics=["Sum"],
)
total = sum(dp["Sum"] for dp in response.get("Datapoints", []))
metrics[metric_name] = total
print(f" {metric_name}: {int(total):,}")
return metrics
def audit_web_acl(client, name, acl_id, scope="REGIONAL"):
"""Run a security audit on a Web ACL configuration."""
acl, _ = get_web_acl_details(client, name, acl_id, scope)
findings = []
rules = acl.get("Rules", [])
rule_names = [r["Name"] for r in rules]
recommended = ["AWSManagedRulesCommonRuleSet", "AWSManagedRulesKnownBadInputsRuleSet",
"AWSManagedRulesSQLiRuleSet", "AWSManagedRulesAmazonIpReputationList"]
for rec in recommended:
if not any(rec in rn for rn in rule_names):
findings.append(f"MISSING: Recommended managed rule group '{rec}' not found")
has_rate_rule = any("RateBasedStatement" in json.dumps(r.get("Statement", {})) for r in rules)
if not has_rate_rule:
findings.append("MISSING: No rate-based rule configured")
default_action = list(acl["DefaultAction"].keys())[0]
if default_action == "Allow":
print(" [INFO] Default action is Allow (standard for WAF)")
print(f"\n[*] Audit findings: {len(findings)}")
for f in findings:
print(f" [!] {f}")
return findings
def main():
parser = argparse.ArgumentParser(description="AWS WAF API Gateway Security Agent")
parser.add_argument("action", choices=["list", "details", "audit", "metrics", "samples", "rate-keys"])
parser.add_argument("--name", help="Web ACL name")
parser.add_argument("--id", help="Web ACL ID")
parser.add_argument("--region", default="us-east-1")
parser.add_argument("--rule-metric", help="Rule metric name for sampling")
parser.add_argument("--rule-name", help="Rate-based rule name")
parser.add_argument("--hours", type=int, default=24, help="Lookback hours for metrics")
args = parser.parse_args()
client = get_waf_client(args.region)
if args.action == "list":
list_web_acls(client)
elif args.action == "details":
acl, _ = get_web_acl_details(client, args.name, args.id)
check_waf_association(client, acl["ARN"])
elif args.action == "audit":
audit_web_acl(client, args.name, args.id)
elif args.action == "metrics":
get_waf_metrics(args.region, args.name or "api-gateway-waf", args.hours)
elif args.action == "samples":
acls = list_web_acls(client)
acl_arn = next((a["ARN"] for a in acls if a["Name"] == args.name), None)
if acl_arn:
get_sampled_requests(client, acl_arn, args.rule_metric or "ALL")
elif args.action == "rate-keys":
get_rate_based_keys(client, args.name, args.id, args.rule_name or "RateLimitPerIP")
if __name__ == "__main__":
main()