performing-api-inventory-and-discovery
执行 API 资产清点和发现,以识别组织环境中的所有 API 端点,包括已记录的、 未记录的、影子 API、僵尸 API 和已废弃的 API。测试人员通过被动流量分析、 主动扫描、DNS 枚举、JavaScript 分析和云资源清点来构建全面的 API 目录。 映射至 OWASP API9:2023 不当资产管理。当请求涉及 API 发现、影子 API 检测、 API 清单审计或攻击面测绘时触发。
Best use case
performing-api-inventory-and-discovery is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
执行 API 资产清点和发现,以识别组织环境中的所有 API 端点,包括已记录的、 未记录的、影子 API、僵尸 API 和已废弃的 API。测试人员通过被动流量分析、 主动扫描、DNS 枚举、JavaScript 分析和云资源清点来构建全面的 API 目录。 映射至 OWASP API9:2023 不当资产管理。当请求涉及 API 发现、影子 API 检测、 API 清单审计或攻击面测绘时触发。
Teams using performing-api-inventory-and-discovery should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/performing-api-inventory-and-discovery/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How performing-api-inventory-and-discovery Compares
| Feature / Agent | performing-api-inventory-and-discovery | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
执行 API 资产清点和发现,以识别组织环境中的所有 API 端点,包括已记录的、 未记录的、影子 API、僵尸 API 和已废弃的 API。测试人员通过被动流量分析、 主动扫描、DNS 枚举、JavaScript 分析和云资源清点来构建全面的 API 目录。 映射至 OWASP API9:2023 不当资产管理。当请求涉及 API 发现、影子 API 检测、 API 清单审计或攻击面测绘时触发。
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
# 执行 API 资产清点与发现
## 适用场景
- 在安全评估前测绘组织的完整 API 攻击面
- 识别由开发团队未经安全审查部署的影子 API(Shadow API)
- 发现仍可访问但无人维护的已废弃或僵尸 API 版本
- 发现通过移动应用、SPA 或微服务暴露的未记录 API 端点
- 为合规需求(PCI-DSS、SOC2、GDPR)建立 API 清单
**不适用于** 未经书面授权的场景。API 发现涉及扫描网络基础设施和分析流量。
## 前置条件
- 明确授权文件,指定目标域名和网络范围
- 被动流量捕获能力(网络分路器、代理或云流量镜像)
- 主动扫描工具:Amass、subfinder、httpx 和 nuclei
- JavaScript 分析工具:LinkFinder、JS-Miner 或自定义解析器
- 访问云控制台(AWS、Azure、GCP)进行 API 网关清点
- Burp Suite Professional 用于被动 API 端点发现
## 工作流程
### 步骤 1:通过流量分析进行被动 API 发现
```python
import re
import json
from collections import defaultdict
# 从浏览器开发者工具或代理解析 HAR 文件
def analyze_har_for_apis(har_file_path):
"""从 HTTP 归档(HAR)文件中提取 API 端点。"""
with open(har_file_path) as f:
har = json.load(f)
api_endpoints = defaultdict(lambda: {
"methods": set(), "content_types": set(),
"auth_types": set(), "count": 0
})
for entry in har["log"]["entries"]:
url = entry["request"]["url"]
method = entry["request"]["method"]
# 识别 API 模式
api_patterns = [
r'/api/', r'/v\d+/', r'/graphql', r'/rest/',
r'/ws/', r'/rpc/', r'/grpc', r'/json',
]
if any(re.search(p, url) for p in api_patterns):
# 规范化 URL(删除查询参数和 ID)
normalized = re.sub(r'\?.*$', '', url)
normalized = re.sub(r'/\d+(/|$)', '/{id}\\1', normalized)
normalized = re.sub(
r'/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}',
'/{uuid}', normalized)
ep = api_endpoints[normalized]
ep["methods"].add(method)
ep["count"] += 1
# 检测认证类型
for header in entry["request"]["headers"]:
name = header["name"].lower()
if name == "authorization":
if "bearer" in header["value"].lower():
ep["auth_types"].add("Bearer/JWT")
elif "basic" in header["value"].lower():
ep["auth_types"].add("Basic")
elif name == "x-api-key":
ep["auth_types"].add("API Key")
# 检测内容类型
content_type = next(
(h["value"] for h in entry["request"]["headers"]
if h["name"].lower() == "content-type"), None)
if content_type:
ep["content_types"].add(content_type.split(";")[0])
print(f"发现 {len(api_endpoints)} 个唯一 API 端点:\n")
for url, info in sorted(api_endpoints.items()):
methods = ", ".join(sorted(info["methods"]))
auth = ", ".join(info["auth_types"]) or "无"
print(f" [{methods}] {url}")
print(f" 认证:{auth} | 请求次数:{info['count']}")
return api_endpoints
```
### 步骤 2:主动 API 端点发现
```bash
# DNS 枚举发现 API 子域名
amass enum -d example.com -o amass_results.txt
subfinder -d example.com -o subfinder_results.txt
# 过滤与 API 相关的子域名
grep -iE '(api|rest|graphql|ws|gateway|backend|internal|staging|dev|v1|v2)' \
amass_results.txt subfinder_results.txt | sort -u > api_subdomains.txt
# 检查哪些子域名处于活跃状态
cat api_subdomains.txt | httpx -status-code -content-length -title \
-tech-detect -o live_apis.txt
# 在每个活跃子域名上探测常见 API 路径
cat api_subdomains.txt | while read domain; do
for path in /api /api/v1 /api/v2 /graphql /swagger.json /openapi.json \
/api-docs /docs /health /status /metrics /actuator; do
curl -s -o /dev/null -w "%{http_code} %{url_effective}\n" \
"https://${domain}${path}" 2>/dev/null | grep -v "^404"
done
done
```
```python
import requests
import concurrent.futures
def discover_api_endpoints(base_domains):
"""主动探测已发现域名上的 API 端点。"""
# 待测试的常见 API 路径
API_PATHS = [
"/api", "/api/v1", "/api/v2", "/api/v3",
"/graphql", "/gql", "/query",
"/rest", "/json", "/rpc",
"/swagger.json", "/swagger/v1/swagger.json",
"/openapi.json", "/openapi.yaml", "/api-docs",
"/docs", "/redoc", "/explorer",
"/.well-known/openid-configuration",
"/health", "/healthz", "/ready",
"/status", "/info", "/version",
"/metrics", "/prometheus",
"/actuator", "/actuator/health", "/actuator/info",
"/admin", "/admin/api", "/internal",
"/debug", "/debug/vars", "/debug/pprof",
"/ws", "/websocket", "/socket.io",
"/grpc", "/twirp",
]
discovered = []
def check_endpoint(domain, path):
for scheme in ["https", "http"]:
url = f"{scheme}://{domain}{path}"
try:
resp = requests.get(url, timeout=5, allow_redirects=False,
verify=False)
if resp.status_code not in (404, 502, 503):
return {
"url": url,
"status": resp.status_code,
"content_type": resp.headers.get("Content-Type", ""),
"server": resp.headers.get("Server", ""),
"size": len(resp.content),
}
except requests.exceptions.RequestException:
pass
return None
with concurrent.futures.ThreadPoolExecutor(max_workers=20) as executor:
futures = {}
for domain in base_domains:
for path in API_PATHS:
future = executor.submit(check_endpoint, domain, path)
futures[future] = (domain, path)
for future in concurrent.futures.as_completed(futures):
result = future.result()
if result:
discovered.append(result)
print(f" [发现] {result['url']} -> {result['status']} ({result['content_type']})")
return discovered
```
### 步骤 3:从 JavaScript 源码分析 API 端点
```python
import re
import requests
def extract_apis_from_javascript(js_urls):
"""从 JavaScript 源文件中提取 API 端点。"""
api_pattern = re.compile(
r'''(?:['"`])((?:/api/|/v[0-9]+/|/graphql|/rest/)[^'"`\s<>{}]+)(?:['"`])''',
re.IGNORECASE
)
url_pattern = re.compile(
r'''(?:['"`])(https?://[a-zA-Z0-9._-]+(?:\.[a-zA-Z]{2,})+(?:/[^'"`\s<>{}]*)?)(?:['"`])'''
)
fetch_pattern = re.compile(
r'''(?:fetch|axios|ajax|XMLHttpRequest|\.get|\.post|\.put|\.delete|\.patch)\s*\(\s*(?:['"`])([^'"`]+)'''
)
all_endpoints = set()
for js_url in js_urls:
try:
resp = requests.get(js_url, timeout=10)
content = resp.text
# 提取相对 API 路径
for match in api_pattern.findall(content):
all_endpoints.add(("relative", match))
# 提取绝对 URL
for match in url_pattern.findall(content):
if any(kw in match.lower() for kw in ["/api", "/v1", "/v2", "graphql"]):
all_endpoints.add(("absolute", match))
# 从 fetch/axios 调用中提取
for match in fetch_pattern.findall(content):
all_endpoints.add(("fetch", match))
except requests.exceptions.RequestException:
pass
print(f"\n从 JavaScript 中发现的 API 端点({len(all_endpoints)} 个):")
for source, endpoint in sorted(all_endpoints):
print(f" [{source}] {endpoint}")
return all_endpoints
# 从目标域名中查找 JavaScript 文件
def find_js_files(domain):
"""从 Web 应用中发现 JavaScript 文件。"""
resp = requests.get(f"https://{domain}", timeout=10)
js_files = re.findall(r'src=["\']([^"\']+\.js[^"\']*)', resp.text)
full_urls = []
for js in js_files:
if js.startswith("http"):
full_urls.append(js)
elif js.startswith("//"):
full_urls.append(f"https:{js}")
elif js.startswith("/"):
full_urls.append(f"https://{domain}{js}")
return full_urls
```
### 步骤 4:云 API 网关清点
```python
import boto3
def inventory_aws_apis():
"""清点 AWS API Gateway 中的所有 API。"""
apigw = boto3.client('apigateway')
apigwv2 = boto3.client('apigatewayv2')
apis = []
# REST API(API Gateway v1)
rest_apis = apigw.get_rest_apis()
for api in rest_apis['items']:
resources = apigw.get_resources(restApiId=api['id'])
stages = apigw.get_stages(restApiId=api['id'])
for stage in stages['item']:
for resource in resources['items']:
for method in resource.get('resourceMethods', {}).keys():
apis.append({
"type": "REST",
"name": api['name'],
"stage": stage['stageName'],
"path": resource['path'],
"method": method,
"url": f"https://{api['id']}.execute-api.{boto3.session.Session().region_name}.amazonaws.com/{stage['stageName']}{resource['path']}",
"created": str(api.get('createdDate', '')),
})
# HTTP API(API Gateway v2)
http_apis = apigwv2.get_apis()
for api in http_apis['Items']:
routes = apigwv2.get_routes(ApiId=api['ApiId'])
stages = apigwv2.get_stages(ApiId=api['ApiId'])
for route in routes['Items']:
apis.append({
"type": "HTTP",
"name": api['Name'],
"route": route['RouteKey'],
"api_id": api['ApiId'],
"protocol": api['ProtocolType'],
})
print(f"\nAWS API 清单({len(apis)} 个端点):")
for api in apis:
print(f" [{api['type']}] {api.get('name')} - {api.get('method', '')} {api.get('path', api.get('route', ''))}")
return apis
```
### 步骤 5:API 版本与影子 API 检测
```python
def detect_shadow_and_zombie_apis(discovered_endpoints, documented_endpoints):
"""将发现的 API 与已记录的清单进行比对。"""
# 规范化端点以便比对
def normalize(ep):
ep = re.sub(r'/v\d+/', '/vX/', ep)
ep = re.sub(r'/\d+', '/{id}', ep)
return ep.lower().rstrip('/')
documented_normalized = {normalize(ep) for ep in documented_endpoints}
shadow_apis = [] # 已发现但未记录
zombie_apis = [] # 旧版本仍可访问
for ep in discovered_endpoints:
normalized = normalize(ep["url"])
if normalized not in documented_normalized:
# 检查是否为已记录 API 的旧版本
if re.search(r'/v[0-9]+/', ep["url"]):
zombie_apis.append(ep)
else:
shadow_apis.append(ep)
print(f"\n影子 API(未记录):{len(shadow_apis)} 个")
for api in shadow_apis:
print(f" [影子] {api['url']} -> {api['status']}")
print(f"\n僵尸 API(已废弃版本):{len(zombie_apis)} 个")
for api in zombie_apis:
print(f" [僵尸] {api['url']} -> {api['status']}")
# 检查僵尸 API 是否缺少安全控制
for api in zombie_apis:
resp = requests.get(api["url"], timeout=5)
if resp.status_code not in (401, 403):
print(f" [严重] 僵尸 API 无需认证即可访问:{api['url']}")
return shadow_apis, zombie_apis
```
## 核心概念
| 术语 | 定义 |
|------|------|
| **影子 API(Shadow API)** | 由开发团队部署,未经过正式 API 管理流程或安全审查的 API |
| **僵尸 API(Zombie API)** | 已废弃或旧版本的 API,仍处于运行状态但不再维护或监控 |
| **API 清单(API Inventory)** | 组织中所有 API 的全面目录,包括端点 URL、所有者、版本、认证方式和数据分类 |
| **资产管理不当(Improper Inventory Management)** | OWASP API9:2023 - 未能维护准确的 API 清单,导致 API 端点未受监控和保护 |
| **攻击面(Attack Surface)** | 攻击者可能与之交互的所有 API 端点、方法和参数的总集合 |
| **API 蔓延(API Sprawl)** | 组织内 API 的无节制扩散,通常由微服务采用而缺乏集中治理所导致 |
## 工具与系统
- **Amass**:通过 DNS 枚举、网页爬取和 API 发现进行攻击面测绘的 OWASP 工具
- **httpx**:用于验证已发现域名并识别活跃 API 端点的快速 HTTP 探测工具
- **nuclei**:基于模板的扫描器,用于检测暴露的 API 文档、调试端点和配置错误的服务
- **Swagger UI Detector**:用于在组织内查找暴露的 Swagger/OpenAPI 文档端点的工具
- **Akto**:通过流量分析发现 API 并维护自动化清单的 API 安全平台
## 常见场景
### 场景:企业 API 攻击面评估
**场景背景**:一家大型企业有 200 多个开发团队使用微服务架构。安全团队怀疑有许多未记录的 API 暴露在互联网上,需要为安全审计进行全面的 API 清点。
**方法**:
1. DNS 枚举发现 340 个子域名,其中 45 个包含 API 相关关键词(api、rest、gateway、backend)
2. 使用 API 路径词典主动探测所有子域名,发现 127 个活跃 API 端点
3. 主 Web 应用的 JavaScript 分析揭示 34 个 API 端点,其中 8 个指向未记录的内部服务
4. AWS API Gateway 清点显示跨 12 个账户有 67 个 REST API 和 23 个 HTTP API
5. 与官方 API 目录交叉对比:31 个影子 API(未记录),14 个僵尸 API(已废弃版本)
6. 3 个僵尸 API 无认证,通过本应下线的端点暴露客户数据
7. 2 个影子 API 无需授权即可向互联网暴露内部管理功能
**常见陷阱**:
- 只检查已记录的 API 端点,遗漏在 API 网关之外部署的影子 API
- 未扫描前端应用程序硬编码 API 端点 URL 的 JavaScript 包
- 遗漏非标准端口或子路径后的 API
- 未检查多个 API 版本,其中旧版本可能缺乏安全控制
- 假设所有 API 都通过 API 网关,而实际上部分 API 可能直接暴露
## 输出格式
```
## API 清单与发现报告
**组织**:示例公司
**评估日期**:2024-12-15
**扫描域名**:340 个
### 摘要
| 类别 | 数量 |
|----------|-------|
| 发现的总 API 数 | 127 |
| 已记录的 API | 82 |
| 影子 API(未记录) | 31 |
| 僵尸 API(已废弃) | 14 |
| 无认证的 API | 8 |
| 暴露敏感数据的 API | 5 |
### 关键发现
1. **僵尸 API**:api-v1.example.com/api/v1/users - 2022 年已废弃,
仍可访问,无需认证,返回完整用户数据
2. **影子 API**:internal-tools.example.com/api/admin - 管理功能
无需授权暴露至互联网
3. **暴露的文档**:12 个 Swagger UI 实例可公开访问,
泄露了完整的 API 模式和端点详情
```Related Skills
performing-yara-rule-development-for-detection
通过识别可执行文件中的唯一字节模式、字符串和行为指标,开发精准的 YARA 恶意软件检测规则,同时将误报率降至最低。
performing-wireless-security-assessment-with-kismet
使用 Kismet 通过被动射频监控进行无线网络安全评估,检测流氓接入点(Rogue AP)、隐藏 SSID、弱加密和未授权客户端。
performing-wireless-network-penetration-test
执行无线网络渗透测试,通过捕获握手包、破解 WPA2/WPA3 密钥、检测流氓接入点以及使用 Aircrack-ng 和相关工具测试无线网络分段,评估 WiFi 安全性。
performing-windows-artifact-analysis-with-eric-zimmerman-tools
使用 Eric Zimmerman 的开源 EZ Tools 套件(包括 KAPE、MFTECmd、PECmd、LECmd、JLECmd 和 Timeline Explorer)执行全面的 Windows 取证制品分析,解析注册表 hive、预取文件、事件日志和文件系统元数据。
performing-wifi-password-cracking-with-aircrack
在授权无线安全评估中捕获 WPA/WPA2 握手包,并使用 aircrack-ng、hashcat 和字典攻击进行离线密码破解, 以评估密码短语强度和无线网络安全状况。
performing-web-cache-poisoning-attack
在授权安全测试期间,通过未纳入缓存键的头部和参数毒化缓存响应,利用 Web 缓存机制向其他用户投递恶意内容。
performing-web-cache-deception-attack
通过利用 CDN 缓存层与源服务器之间的路径规范化差异,执行 Web 缓存欺骗攻击,从而缓存并获取敏感的已认证内容。
performing-web-application-vulnerability-triage
使用 OWASP 风险评级方法论对 DAST/SAST 扫描器的 Web 应用程序漏洞发现进行分类,区分真阳性和假阳性,并确定修复优先级。
performing-web-application-scanning-with-nikto
Nikto 是一款开源 Web 服务器和 Web 应用程序扫描器,可针对超过 7,000 个潜在危险文件/程序进行测试,检查超过 1,250 个服务器的过期版本,并识别超过 270 个服务器的版本特定问题。
performing-web-application-penetration-test
遵循 OWASP Web 安全测试指南(WSTG)方法论,对 Web 应用程序执行系统化安全测试,识别认证、授权、 输入验证、会话管理和业务逻辑中的漏洞。测试人员以 Burp Suite 作为主要拦截代理,结合手动测试技术 发现自动化扫描器遗漏的缺陷。适用于 Web 应用渗透测试、OWASP 测试、应用安全评估或 Web 漏洞测试等请求场景。
performing-web-application-firewall-bypass
使用编码技术、HTTP 方法操控、参数污染和载荷混淆绕过 Web 应用防火墙保护,将 SQL 注入、XSS 及其他攻击载荷穿透 WAF 检测规则。
performing-vulnerability-scanning-with-nessus
使用 Tenable Nessus 执行认证和未认证漏洞扫描,识别网络基础设施、服务器和应用程序中的已知漏洞、 错误配置、默认凭据和缺失补丁。扫描器将发现与 CVE 数据库和 CVSS 评分关联,生成优先级修复指导。 适用于漏洞扫描、Nessus 评估、补丁合规检查或自动化漏洞检测等请求场景。