15,465 个 MCP 服务器,0 治理:OX Security 的审计对你的 Agent 栈意味着什么
企业安全团队花了十年时间,围绕云环境建立治理:数据驻留要求、零信任边界、精细的 IAM 策略、供应链审计。这些问题的答案都有一个前提——你大概知道数据落在哪里、谁在运营那台基础设施。2026 年 9 月 24 日,OX Security 发布了一份名为《15,465 MCP Servers, 0 Governance》的研究,结论是:Model Context Protocol 正在让大部分这类治理失效。MCP 让 AI Agent、开发者工作站和自动化流水线按需连上第三方服务器,这很有用,但很多时候它就是一套影子 AI 基础设施。
一、这项研究到底测了什么
研究的方法很直接:从三个公开注册表——mcp-official-registry、cline-marketplace 和 github-mcp-registry——收集了 15,465 个已发布的 MCP 服务器,然后收窄到 5,095 个唯一主机名做基础设施分析。MCP 是 Anthropic 在 2024 年 11 月引入的开放标准,让 AI 应用连接外部工具与数据源。但正如 OX 所指出的,MCP 本身并不规定服务器应该跑在哪里、由谁运营、可以接收什么数据,也不保证一个在线服务器背后部署的代码和它公开的源码一致。这些决定基本都甩给了接入方。研究负责人 Moshe Siman Tov Bustan 的总结很到位:你能审查代码,但服务器可能是另一个版本;你也不知道谁控制着它、它收集什么数据、未来的更新会改什么。
# Finding the MCP servers you are actually running is step one.
# Every config file, every IDE, every pipeline is a candidate.
import json, pathlib
CONFIG_HINTS = [".mcp.json", "mcp.json", ".cursor/mcp.json",
".vscode/mcp.json", "claude_desktop_config.json"]
def inventory(root: pathlib.Path):
found = []
for path in root.rglob("*"):
if path.name in [p.split("/")[-1] for p in CONFIG_HINTS] and path.is_file():
try:
cfg = json.loads(path.read_text())
except Exception:
continue
for name, spec in (cfg.get("mcpServers") or {}).items():
found.append({
"server": name,
"command": spec.get("command"),
"url": spec.get("url"),
"declared_in": str(path.relative_to(root)),
})
return found
for row in inventory(pathlib.Path.home()):
print(row["server"], row["url"] or row["command"])MCP 让 Agent 按需连上第三方服务器
二、发现之一:数据驻留的盲区
第一个发现是数据驻留盲区。在 5,095 个主机名中,15.6%(也就是 796 个)解析到美国境外的基础设施,其中 19 个在中国、18 个在俄罗斯。关键在于,MCP 在协议层面根本没有地理区域的概念。一家企业可以在自己的云工作负载上执行严格的数据驻留控制,同时它的 AI Agent 却自由地连向这些控制之外的服务器。示例 3 给出了把这件看不见的事变成可查询事实的做法:把每个主机名做解析与注册信息核查,判断它是否仍然存在、注册时间与到期时间,并把「最近注册的便宜域名」标出来复核。
// Governance gate 1: egress is deny-by-default. MCP has no protocol-level
// concept of geography, so the boundary has to live in your network.
const mcpEgressPolicy = {
default: "deny",
rules: [
{ server: "github-mcp", allow: ["api.github.com"], ports: [443] },
{ server: "postgres-mcp", allow: ["db.internal.corp"], ports: [5432] },
],
// Anything that does not match is blocked AND recorded. A blocked
// request you can see beats an allowed request you never noticed.
onBlock: (req) => audit.record({ kind: "mcp_egress_blocked", ...req }),
};
export function checkEgress(server, host, port) {
const rule = mcpEgressPolicy.rules.find((r) => r.server === server);
if (!rule || !rule.allow.includes(host) || !rule.ports.includes(port)) {
mcpEgressPolicy.onBlock({ server, host, port });
return false;
}
return true;
}三、发现之二与之三:家庭网络与废弃域名
第二个和第三个发现更贴近运维现实。0.45% 的主机名经由消费级 ISP 网络或个人内网穿透工具路由,也就是说,生产级 AI 工作流可能依赖着没有可用性保障、没有企业访问控制、也没有真正可审计性的基础设施——因为它本来就不是按企业基建造的。另一头,2.3% 的主机名已经无法解析,其中六个域名处于未注册状态,年费只要 4 到 12 美元。这些域名很可能仍被写在某些人的配置或流水线里。任何人都可以买下它们,然后开始冒充它原本指向的服务器。这是最经典的一类接管路径:信任关系还在,被信任的对象已经易主。
#!/usr/bin/env python3
# Governance gate 2: resolve every hostname. Where does it live, does it
# still exist, and could someone else point it somewhere new tomorrow?
import socket, whois # whois via python-whois
def classify(hostname):
try:
ip = socket.gethostbyname(hostname)
except socket.gaierror:
return {"hostname": hostname, "status": "does_not_resolve",
"risk": "abandoned domain may be re-registered"}
record = whois.whois(hostname)
created = record.creation_date
expires = record.expiration_date
return {
"hostname": hostname,
"ip": ip,
"status": "resolves",
"created": str(created),
"expires": str(expires),
# Cheap, recently created domains deserve a second look before
# you let an agent send them your code and credentials.
"risk": "review" if is_recent(created) else "ok",
}
for host in reported_hostnames:
print(classify(host))5,095 个主机名里有 796 个解析到美国境外
四、发现之四:比一次授权活得更久的信任
第四个发现最值得安全团队警惕。OX 做了一个基于「信任」的提示注入测试:他们拿 Claude Code 搭配 Haiku 3.5,让一个恶意 MCP 服务器先请求访问一个无害文件;用户以 always-allow 批准。随后服务器请求一个敏感文件,其中包括 .env,并没有再弹窗就直接拿到了。同一个攻击在 Opus 4.6 和 4.7 上被检测并拦截。Anthropic 的回应是:一旦授予 always-allow,这就是有文档记录的行为;而模型层面对恶意内容的检测是尽力而为的启发式,不是安全边界。这句话值得反复读:你用一次点击换来的便利,会变成一次永久的授权。
// Governance gate 3: never grant always-allow. One benign approval should
// not become permanent access to every file the server asks for later.
// The OX Security test showed a single always-allow on a harmless file
// unlocked a sensitive .env read with no further prompt.
const permissionModel = {
// Approvals are per (server, resource), never per server.
grant: (server, resource) => ({
server,
resource, // exact path/pattern, not "*"
expiresInMinutes: 30, // time-boxed, not permanent
maxUses: 1, // single use unless reviewed
}),
deny: ["**/.env*", "**/.ssh/**", "**/id_rsa*", "**/.aws/**", "**/secrets/**"],
alwaysAllow: false, // this whole option is off
onRequest: async (req) => {
if (permissionModel.deny.some((p) => minimatch(req.resource, p))) {
return deny(req);
}
return promptHuman(req); // fresh consent, every time
},
};五、从部署前评审转向运行时控制
把这些发现放到一起,结论并不是「不要用 MCP」——MCP 的价值是真实的,规模也在飞速增长。结论是:治理必须从「部署前评审」变成「运行时控制」。示例 2 把出口做成默认拒绝的白名单:MCP 没有协议级的地理概念,边界就必须落在你的网络里;每条规则按服务器和端口细化,任何不匹配的请求不仅被拦截,还要写进审计。示例 4 则把工具调用和目的地绑定记录在一起,这样「哪个 Agent 把数据发到了我们批准区域之外」就变成一句可执行的查询,而不是一次事故调查。
# Governance gate 4: log the tool call and the destination together, so a
# question like "which agent talked to a host outside our region?" is a query
# rather than an incident investigation.
def log_tool_call(session, call, destination, decision):
ledger.append({
"ts": time.time(),
"agent": session.agent,
"server": call.server,
"tool": call.tool, # e.g. read_file, run_query
"destination": destination.host, # where the bytes went
"residency": destination.region, # us | eu | other | unknown
"decision": decision, # allow | block | ask
"resourceHash": call.resource_hash,
"approvedBy": decision.approver,
})
# The monthly question: how much traffic left our approved regions?
outside = [r for r in ledger.tail(10000) if r["residency"] not in ("us", "eu")]
print(len(outside), "tool calls reached unapproved regions")未声明的出口应当产生审计记录,而不是静默通过
六、一份七步落地清单
一份可落地的清单:第一,先清点——你有多少 MCP 服务器、分别写在哪些配置里、连向哪些主机,示例 1 给出了从配置中发现服务器的做法。第二,出口默认拒绝,任何未声明的目的地都产生审计记录。第三,对每个主机名做解析与注册核查,把不再解析的域名挑出来并阻断,把最近注册的低价域名列入复核。第四,禁止 always-allow,把授权改成「按服务器 + 按资源」、带时限、单次使用。第五,为每个 MCP 服务器使用独立凭据并收窄作用域,别让一个服务器拿着你全量的令牌。第六,把工具调用与出口目的地一起留痕,并定期出「离开批准区域的调用数」这类报表。第七,把 MCP 加进采购与供应商评审,问清楚谁运营服务器、数据去哪里、更新由谁控制。
📌 常见问题 FAQ
OX Security 这项研究分析了多少 MCP 服务器?
研究从三个公开注册表收集了 15,465 个已发布的 MCP 服务器,并收窄到 5,095 个唯一主机名进行基础设施分析。
有多少 MCP 主机名解析到美国境外?
15.6%(5,095 个中的 796 个)解析到美国境外的基础设施,其中包括 19 个在中国、18 个在俄罗斯。MCP 在协议层面没有地理区域的概念。
什么是「废弃域名」风险?
2.3% 的主机名已无法解析,其中六个域名未注册,年费仅 4 到 12 美元。它们可能仍写在配置或流水线里,任何人都能买下并冒充原端点。
always-allow 权限有什么问题?
OX 的测试显示,用 Claude Code 搭配 Haiku 3.5 时,一次针对无害文件的 always-allow 批准会让恶意 MCP 服务器在不再弹窗的情况下读取 .env 等敏感文件。该攻击在 Opus 4.6 和 4.7 上被拦截。
组织应该怎样降低 MCP 风险?
把治理从部署前评审转向运行时控制:出口默认拒绝、按主机名核查解析与注册、禁止 always-allow、为每个服务器使用独立凭据,并把工具调用与出口目的地一起留痕。
🔧 推荐工具
📚 参考资料
- OX Security — How MCP Is Bypassing a Decade of Cloud Security Best Practices (Sep 24, 2026)
- PR Newswire — New Research: MCP Servers Connect AI Agents to China, Russia, Home Networks and Abandoned Domains (Sep 24, 2026)
- OX Security — Report: 15,465 MCP Servers, 0 Governance
- Model Context Protocol — Specification 2026-07-28
- OX Security — The Mother of All AI Supply Chains (MCP STDIO RCE research)