AWS WAF ASN Match 实战:基于自治系统编号的精准流量控制¶
Lab 信息
- 难度: ⭐⭐ 中级
- 预估时间: 45 分钟
- 预估费用: < $0.50(含清理)
- Region: us-east-1
- 最后验证: 2026-03-26
背景¶
在 Web 安全防护中,封锁恶意流量的传统方式是维护 IP 黑名单。但 IP 地址频繁变化,一个大型 ISP 或云服务商可能拥有数以万计的 IP 段。AWS WAF 新增的 ASN Match Statement 让你可以直接基于 ASN(自治系统编号)来匹配和过滤流量 —— 一条规则就能覆盖整个网络组织的所有 IP,无需维护庞大的 IP 列表。
典型使用场景:
- 封锁特定 ISP / 托管商 / 云服务商的流量
- 仅允许来自可信合作伙伴网络的访问
- 结合 Rate-based 规则,按 ASN 维度做流量限速
前置条件¶
- AWS 账号(需要 WAF、ELBv2、Lambda、IAM 权限)
- AWS CLI v2 已配置
- 一个可用的 ALB(本文会创建测试用 ALB)
核心概念¶
ASN 是什么?¶
ASN(Autonomous System Number)是分配给大型网络的唯一标识符。每个 ISP、大型企业、云服务商都有自己的 ASN。例如:
- AS16509 — Amazon.com, Inc.
- AS15169 — Google LLC
- AS13335 — Cloudflare, Inc.
ASN Match vs IP Set vs Geo Match¶
| 维度 | ASN Match | IP Set | Geo Match |
|---|---|---|---|
| 粒度 | 网络组织级别 | 单 IP/CIDR | 国家/地区 |
| 最大条目 | 100 ASN/规则 | 10,000 IP/IP Set | 50 国家/规则 |
| WCU | 1 | 1 | 1 |
| 维护成本 | 低(ASN 稳定) | 高(IP 频繁变化) | 低 |
| 适用场景 | 封锁特定 ISP/云商 | 精确 IP 控制 | 合规/地理封锁 |
| Rate-based 聚合 | ✅ | ✅ | ❌ |
关键限制¶
- 每条规则最多 100 个 ASN
- ASN 有效范围:0 ~ 4,294,967,295
- ASN 0 代表无法映射 ASN 的 IP(特殊值)
- WCU 消耗:ASN Match 规则 = 1 WCU;Rate-based + ASN 聚合 = 32 WCU
动手实践¶
Step 1: 准备测试环境¶
创建 Lambda 后端 + ALB 作为测试目标。
# 设置环境变量
export AWS_PROFILE=your-profile
export AWS_REGION=us-east-1
export VPC_ID=your-vpc-id # 使用默认 VPC 即可
创建 Lambda 函数:
# 创建 Lambda 代码
cat > /tmp/waf-asn-lambda.py << 'EOF'
import json
def handler(event, context):
return {
"statusCode": 200,
"statusDescription": "200 OK",
"isBase64Encoded": False,
"headers": {"Content-Type": "application/json"},
"body": json.dumps({
"message": "Hello from WAF ASN Test!",
"sourceIp": event.get("headers", {}).get("x-forwarded-for", "unknown")
})
}
EOF
cd /tmp && zip -j waf-asn-lambda.zip waf-asn-lambda.py
# 创建 IAM 角色
aws iam create-role --role-name waf-asn-test-lambda-role \
--assume-role-policy-document '{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {"Service": "lambda.amazonaws.com"},
"Action": "sts:AssumeRole"
}]
}'
sleep 10 # 等待角色传播
# 创建 Lambda 函数
ROLE_ARN=$(aws iam get-role --role-name waf-asn-test-lambda-role \
--query 'Role.Arn' --output text)
aws lambda create-function \
--function-name waf-asn-test-handler \
--runtime python3.12 \
--handler waf-asn-lambda.handler \
--role $ROLE_ARN \
--zip-file fileb:///tmp/waf-asn-lambda.zip \
--timeout 10 --memory-size 128
创建 Security Group 和 ALB:
# 获取你的公网 IP
MY_IP=$(curl -s https://checkip.amazonaws.com)
# 创建 Security Group(仅允许你的 IP 访问,不要用 0.0.0.0/0)
SG_ID=$(aws ec2 create-security-group \
--group-name waf-asn-test-sg \
--description "WAF ASN test ALB SG" \
--vpc-id $VPC_ID \
--query 'GroupId' --output text)
aws ec2 authorize-security-group-ingress \
--group-id $SG_ID --protocol tcp --port 80 --cidr ${MY_IP}/32
# 创建 ALB(选择 2 个 AZ 的子网)
SUBNETS="subnet-aaaa subnet-bbbb" # 替换为你的公有子网
ALB_ARN=$(aws elbv2 create-load-balancer \
--name waf-asn-test-alb \
--subnets $SUBNETS \
--security-groups $SG_ID \
--scheme internet-facing --type application \
--query 'LoadBalancers[0].LoadBalancerArn' --output text)
# 创建 Target Group + 注册 Lambda
TG_ARN=$(aws elbv2 create-target-group \
--name waf-asn-test-tg --target-type lambda \
--query 'TargetGroups[0].TargetGroupArn' --output text)
LAMBDA_ARN=$(aws lambda get-function --function-name waf-asn-test-handler \
--query 'Configuration.FunctionArn' --output text)
aws lambda add-permission --function-name waf-asn-test-handler \
--statement-id alb-invoke --action lambda:InvokeFunction \
--principal elasticloadbalancing.amazonaws.com
aws elbv2 register-targets --target-group-arn $TG_ARN \
--targets Id=$LAMBDA_ARN
# 创建 Listener
aws elbv2 create-listener --load-balancer-arn $ALB_ARN \
--protocol HTTP --port 80 \
--default-actions Type=forward,TargetGroupArn=$TG_ARN
# 等待 ALB 就绪
aws elbv2 wait load-balancer-available --load-balancer-arns $ALB_ARN
# 获取 ALB DNS 名称
ALB_DNS=$(aws elbv2 describe-load-balancers \
--load-balancer-arns $ALB_ARN \
--query 'LoadBalancers[0].DNSName' --output text)
echo "ALB ready: http://$ALB_DNS"
验证 ALB 正常工作:
Step 2: 查找你的 ASN¶
Step 3: 创建 ASN Match Block 规则¶
创建 WebACL,封锁你的 ASN(用于验证功能):
cat > /tmp/waf-asn-block.json << 'EOF'
{
"Name": "waf-asn-test-block",
"Scope": "REGIONAL",
"DefaultAction": {"Allow": {}},
"Rules": [
{
"Name": "block-my-asn",
"Priority": 1,
"Statement": {
"AsnMatchStatement": {
"AsnList": [16509]
}
},
"Action": {"Block": {}},
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "block-my-asn"
}
}
],
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "waf-asn-test-block"
}
}
EOF
# 创建 WebACL
ACL_ARN=$(aws wafv2 create-web-acl \
--cli-input-json file:///tmp/waf-asn-block.json \
--query 'Summary.ARN' --output text)
# 关联到 ALB(新建 WebACL 需要等待 30-120 秒)
sleep 30
aws wafv2 associate-web-acl --web-acl-arn $ACL_ARN --resource-arn $ALB_ARN
验证 Block 效果:
Step 4: ASN + Geo Match 组合规则¶
ASN Match 支持嵌套,可以和 Geo Match 组合使用 AND/OR 逻辑:
cat > /tmp/waf-asn-and-geo.json << 'EOF'
{
"Name": "waf-asn-test-and-geo",
"Scope": "REGIONAL",
"DefaultAction": {"Allow": {}},
"Rules": [
{
"Name": "block-asn-and-country",
"Priority": 1,
"Statement": {
"AndStatement": {
"Statements": [
{
"AsnMatchStatement": {
"AsnList": [16509]
}
},
{
"GeoMatchStatement": {
"CountryCodes": ["SG"]
}
}
]
}
},
"Action": {"Block": {}},
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "block-asn-and-country"
}
}
],
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "waf-asn-test-and-geo"
}
}
EOF
aws wafv2 create-web-acl --cli-input-json file:///tmp/waf-asn-and-geo.json
Step 5: ForwardedIPConfig — 从 X-Forwarded-For 提取 ASN¶
当流量经过 CDN 或代理时,真实客户端 IP 在 X-Forwarded-For header 中:
cat > /tmp/waf-asn-xff.json << 'EOF'
{
"Name": "waf-asn-test-xff",
"Scope": "REGIONAL",
"DefaultAction": {"Allow": {}},
"Rules": [
{
"Name": "block-asn-via-xff",
"Priority": 1,
"Statement": {
"AsnMatchStatement": {
"AsnList": [16509],
"ForwardedIPConfig": {
"HeaderName": "X-Forwarded-For",
"FallbackBehavior": "NO_MATCH"
}
}
},
"Action": {"Block": {}},
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "block-asn-via-xff"
}
}
],
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "waf-asn-test-xff"
}
}
EOF
验证 ForwardedIPConfig 行为:
# 带 Amazon IP 的 XFF → 403(ASN 16509 匹配)
curl -s -o /dev/null -w "%{http_code}\n" -H "X-Forwarded-For: 54.239.28.85" http://$ALB_DNS/
# 带 Google IP 的 XFF → 200(ASN 15169 不匹配)
curl -s -o /dev/null -w "%{http_code}\n" -H "X-Forwarded-For: 8.8.8.8" http://$ALB_DNS/
# 无 XFF header → 200(FallbackBehavior=NO_MATCH)
curl -s -o /dev/null -w "%{http_code}\n" http://$ALB_DNS/
Step 6: Rate-based + ASN 聚合¶
以 ASN 作为聚合维度做频率限制:
cat > /tmp/waf-asn-rate.json << 'EOF'
{
"Name": "waf-asn-test-rate",
"Scope": "REGIONAL",
"DefaultAction": {"Allow": {}},
"Rules": [
{
"Name": "rate-limit-by-asn",
"Priority": 1,
"Statement": {
"RateBasedStatement": {
"Limit": 100,
"EvaluationWindowSec": 60,
"AggregateKeyType": "CUSTOM_KEYS",
"CustomKeys": [
{
"ASN": {}
}
]
}
},
"Action": {"Block": {}},
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "rate-limit-by-asn"
}
}
],
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "waf-asn-test-rate"
}
}
EOF
验证 WCU 消耗:
aws wafv2 check-capacity --scope REGIONAL \
--rules '[{"Name":"rate-by-asn","Priority":1,"Statement":{"RateBasedStatement":{"Limit":100,"EvaluationWindowSec":60,"AggregateKeyType":"CUSTOM_KEYS","CustomKeys":[{"ASN":{}}]}},"Action":{"Block":{}},"VisibilityConfig":{"SampledRequestsEnabled":true,"CloudWatchMetricsEnabled":true,"MetricName":"rate-by-asn"}}]'
# 输出: {"Capacity": 32} — 2(base) + 30(ASN key) = 32 WCU
测试结果¶
功能验证汇总¶
| # | 测试场景 | 预期 | 实际 | 状态 |
|---|---|---|---|---|
| 1 | ASN Match Block(封锁 ASN 16509) | 403 | 403 | ✅ |
| 2 | ASN Match Allow + 默认 Block | 200 | 200 | ✅ |
| 3 | AND(ASN + Geo) 组合规则 | 403 | 403 | ✅ |
| 4 | Rate-based + ASN 聚合(WCU 验证) | 32 WCU | 32 WCU | ✅ |
| 5 | ASN 0 不匹配已知 ASN | 200 | 200 | ✅ |
| 6 | 100 ASN 上限(100 通过 / 101 拒绝) | 100✅ 101❌ | 100✅ 101❌ | ✅ |
| 7 | ForwardedIPConfig(XFF header) | 按 XFF 中 IP 的 ASN 匹配 | 符合预期 | ✅ |
ForwardedIPConfig 详细结果¶
| 场景 | XFF Header | 预期 | 实际 |
|---|---|---|---|
| Amazon IP | 54.239.28.85 (ASN 16509) |
403 | 403 |
| Google IP | 8.8.8.8 (ASN 15169) |
200 | 200 |
| 无 Header | —(FallbackBehavior=NO_MATCH) | 200 | 200 |
WCU 消耗对比¶
| 规则类型 | WCU |
|---|---|
| 单纯 ASN Match | 1 |
| Rate-based + ASN 聚合 | 32 (2 + 30) |
| AND(ASN + Geo) | 2 (1 + 1) |
踩坑记录¶
WebACL 关联延迟
新创建的 WebACL 通过 associate-web-acl 关联到 ALB 时,可能需要等待 30-120 秒。在此期间会收到 WAFUnavailableEntityException 错误,需要重试。建议在脚本中加入 sleep 30 和重试逻辑。(实测发现,官方文档仅提示 "Retry your request",未说明具体等待时间。)
Rate-based 规则非实时生效
Rate-based 规则有 1-5 分钟的评估延迟,不是请求超限后立即生效。适合防护持续性攻击,不适合精确的瞬时限流。(已查文档确认:WAF 在评估窗口内统计请求。)
GetRateBasedStatementManagedKeys 限制
使用 CUSTOM_KEYS(如 ASN 聚合)的 Rate-based 规则,无法通过 GetRateBasedStatementManagedKeys API 查看已限流的键。此 API 仅支持 AggregateKeyType 为 IP 或 FORWARDED_IP 的规则。(已查文档确认。)
费用明细¶
| 资源 | 单价 | 用量 | 费用 |
|---|---|---|---|
| WebACL | $5/月 | ~1 小时 | ~$0.007 |
| 规则 | $1/月/规则 | ~1 小时 | ~$0.001 |
| ALB | $0.0225/hr | ~1 小时 | $0.023 |
| Lambda | — | 免费额度内 | $0 |
| WAF 请求 | $0.60/百万 | <1000 | ~$0 |
| 合计 | < $0.05 |
清理资源¶
# 1. 解除 WebACL 关联
aws wafv2 disassociate-web-acl --resource-arn $ALB_ARN
# 2. 删除所有 WebACL(需要获取每个的 Id 和 LockToken)
# 列出所有 WebACL
aws wafv2 list-web-acls --scope REGIONAL
# 对每个 WebACL 执行:
aws wafv2 delete-web-acl --name NAME --scope REGIONAL --id ID --lock-token LOCK_TOKEN
# 3. 删除 ALB
aws elbv2 delete-listener --listener-arn $LISTENER_ARN
aws elbv2 delete-load-balancer --load-balancer-arn $ALB_ARN
aws elbv2 delete-target-group --target-group-arn $TG_ARN
# 4. 删除 Lambda
aws lambda delete-function --function-name waf-asn-test-handler
# 5. 删除 IAM 角色
aws iam delete-role --role-name waf-asn-test-lambda-role
# 6. 等待 ALB 完全删除后,删除 Security Group
sleep 60
aws ec2 delete-security-group --group-id $SG_ID
务必清理
Lab 完成后请执行清理步骤,避免产生意外费用。ALB 按小时计费($0.0225/hr),WebACL 按月计费($5/月)。
结论与建议¶
适用场景¶
- 封锁特定 ISP/托管商 — 当某个 ISP 持续产生恶意流量时,一条 ASN 规则覆盖其所有 IP
- 白名单模式 — 仅允许可信合作伙伴网络访问,配合默认 Block
- 分层防御 — AND(ASN + Geo) 组合使用,精准定位流量来源
- 按网络维度限速 — Rate-based + ASN 聚合,按运营商级别限速
与 IP Set 的选择建议¶
- IP 段明确、需要精确控制 → IP Set
- 封锁整个 ISP/组织、减少维护成本 → ASN Match
- 两者可以同时使用,ASN 做粗粒度、IP Set 做细粒度
生产环境建议¶
- 先用 Count 模式观察 ASN 匹配情况,确认无误后切换为 Block
- 大型 ASN(如 AWS 自身的 AS16509)覆盖面广,Block 前务必确认影响范围
- 利用 ASN 0 处理无法映射的 IP(如私有 IP),避免安全漏洞
- ForwardedIPConfig 的 FallbackBehavior 建议设为 NO_MATCH(安全侧),避免因缺失 header 误封