本页内容
2026 年 7 月,一位安全研究者用一套专门打造的扫描器,对准了他们能在公网上找到的所有 MCP server。他们确认了 640 个生产环境服务器,对其中 414 个进行了动态审计,并发现了 68 个可上报的漏洞 —— SQL 注入、prompt 模板注入、路径遍历,以及直指云元数据服务的 SSRF。
真正该让你停下来的数字是:在他们审计的服务器中,91.8% 完全没有 OAuth 认证。
如果你开发或运行一个网页抓取类 MCP server,这件事对你的意义比对任何人都大。抓取工具的全部职责就是接收一个 URL 并把它取回来。当这个 URL 由语言模型来选择,而这个模型又会受到它所读取页面内容的影响时,你就等于造好了一台服务端请求伪造机器,并把方向盘交到了攻击者手上。
目录
- 2026 年 MCP server 安全现状
- 为什么抓取类服务器是最糟的情况
- 一个真实 CVE 的剖析
- 绕过阶梯:URL 检查失效的六种方式
- 一份真正站得住脚的防御清单
- CrawlForge 是怎么做的
- 如果你是使用者而非开发者
2026 年 MCP server 安全现状
这项研究是 Exposed by Design: A Dynamic Security Assessment of Internet-Facing MCP Servers at Scale(Nicolás Padilla,arXiv:2608.00150,2026 年 7 月 31 日提交)。它是第一份针对真实环境中 MCP server 的动态行为评估,而不是对源代码的静态审阅。
其方法是先通过十一个数据源进行被动发现 —— 证书透明度日志、GitHub、npm、PyPI、HuggingFace、Smithery、Censys、FOFA、Shodan 等 —— 再用 Corvus 进行主动测试,那是一套包含 34 个测试模块、覆盖 10 类 MCP 专有漏洞的框架。
四轮测量的结果如下:
| 发现 | 数字 |
|---|---|
| 公网上可检测到的 MCP server 实例 | 超过 21,000 |
| 确认的生产环境服务器 | 640 |
| 经过动态审计的服务器 | 414 |
| 发现的可上报漏洞 | 68 |
| 完全没有 OAuth 认证 的受审服务器 | 91.8% |
| 无访问控制即暴露 shell 执行能力的工具实例 | 687 |
| 三天内消失的已确认服务器 | 41.6% |
最后一行是大家最容易略过、却可能最能说明问题的一条。在连续两轮测量之间,有四成服务器消失了 —— 这正是软件被直接部署上公网、未经安全审查、随后又被撤下的典型特征。
为什么抓取类服务器是最糟的情况
大多数关于 SSRF 的建议都假设场景是一个 Web 应用,攻击者必须先找到某个会在服务端被取回的隐蔽参数。而抓取类 MCP server 把这一点彻底反转了:取回攻击者提供的 URL 正是它对外宣传的功能。
有三个特性叠加在一起,效果很糟:
URL 由模型控制。 你的工具 schema 写着 url: string,由模型来填写。协议中没有任何东西能区分「用户输入的 URL」和「模型自己编出来的 URL」。
模型会读取不可信的文本。 正是这一点,把一个设计特性变成了可利用的漏洞。你的 agent 抓取了一个页面;该页面包含一段文本,指示模型去获取 http://169.254.169.254/latest/meta-data/iam/security-credentials/;模型照做了。这就是把 prompt 注入直接转化成了 SSRF,而且请求是从你的网络内部发出的,带着你的实例所持有的一切凭据。
输出会直接回到模型的上下文中。 经典的 SSRF 是盲打的 —— 你往往看不到响应。而在这里,响应体会作为工具输出返回给模型,并由此进入对话、日志,甚至可能进入下一次工具调用。数据外泄是自带的。
云元数据端点是显而易见的目标,因为它们在设计上无需认证,且在各大云厂商上都位于一个固定且众所周知的地址。但同样的原语也能触及内部管理面板、localhost 上的 Redis、Kubernetes API 服务器,以及其他一切曾经信任网络边界的东西。
一个真实 CVE 的剖析
CVE-2026-65056(2026 年 7 月 21 日发布,CWE-918,CVSS 4.0 基础评分 8.3 HIGH)在一个真实的软件包中完整描述了这条链路。受影响的软件是 mcp-webresearch 0.1.7 及以下版本。
根据 NVD 记录,该缺陷在于 visit_page 工具只校验 URL 协议 —— 它检查你传入的是 http: 还是 https:,却从不过滤私有或保留 IP 段。随后该通告完整列出了整条链路:攻击者通过 prompt 注入操纵由 LLM 控制的 URL 参数,服务器的 Playwright 浏览器导航到诸如云实例元数据服务之类的内部端点,而那个内部页面的敏感内容 —— 包括凭据 —— 被返回到模型的上下文中。
这不是理论上的攻击路径。这就是该 CVE 的描述原文。
它也并非孤例。CVE-2026-26118 是 微软自家 Azure MCP Server 中的服务端请求伪造,评分为 CVSS 3.1 8.8 HIGH,同样是 CWE-918,已在 1.0.2 和 2.0.0-beta.17 中修复。如果连微软都在官方 MCP server 中发布过 SSRF,那么一个周末做出来的抓取服务器能把它做对的概率并不乐观。
绕过阶梯:URL 检查失效的六种方式
接下来是令人不适的部分。大多数确实添加了 SSRF 防护的开发者,只加到了这道阶梯的第一或第二级就停下了。下面每一级都是对上一级的真实绕过。
1. 只校验协议。 只检查 http:/https:,别的什么都不查。这正是 CVE-2026-65056 的情形。它能挡住 file:// 和 gopher://,却挡不住这里真正要紧的任何东西。
2. 主机名字符串黑名单。 屏蔽字面字符串 localhost 和 127.0.0.1。这可以被轻易绕过,因为一个 IP 地址有许多种写法。
3. IP 字面量的替代编码。 127.0.0.1 也可以写成十进制的 2130706433 和十六进制的 0x7f000001。WHATWG URL 解析器 —— 也就是每个现代运行时内置的那一个 —— 会在你的字符串检查已经通过之后,把这些全部规范化为同一个地址。更糟的是,在 Node.js 中 IP 字面量根本不会经过 DNS 解析,因此挂在 lookup 调用上的防护什么也看不到。
4. IPv4 映射的 IPv6。 ::ffff:127.0.0.1 和 ::ffff:169.254.169.254 把一个 IPv4 地址嵌进了 IPv6 记法里。只理解点分四段 IPv4 的范围检查会直接放行。这还打开了一个由 DNS 控制的变种:攻击者发布一条指向被映射内部地址的 AAAA 记录。
5. DNS 重绑定(TOCTOU)。 你解析主机名,确认它是公网地址,批准放行 —— 然后 HTTP 客户端在真正建立连接时又解析了一次。借助很短的 TTL,攻击者会在第一次查询时返回公网 IP,在第二次时返回内部 IP。唯一持久有效的修复是先检查,然后把连接固定到已验证的那个 IP 上,让你批准的地址就是你实际连接的地址。
6. 重定向跳转。 你校验了别人给你的那个 URL。服务器返回 302 Location: http://169.254.169.254/。除非每一跳都重新校验 —— 并且除非被允许的第一跳不会悄悄让链路上的其余部分失去防护 —— 否则你只是把漏洞往下游挪了一步而已。
还有第七种情况专属于浏览器自动化:如果你的工具驱动 Playwright 或 Puppeteer,浏览器会自行执行导航。给你的 fetch 包装层加防护毫无用处。你需要一次导航前检查以及一次对实际 URL 的导航后重新读取,因为客户端重定向和 meta-refresh 都发生在页面内部。
一份真正站得住脚的防御清单
[ ] Block by resolved IP range, never by hostname string
[ ] Run the range check on IP-literal hosts too — they skip DNS entirely
[ ] Normalize IPv4-mapped IPv6 before any range comparison
[ ] Pin the connection to the validated IP (defeats DNS rebinding)
[ ] Re-validate every redirect hop, not just the first URL
[ ] Scope allowlists per-hop, so one trusted host does not unguard the chain
[ ] Guard browser navigation separately, with a post-navigation URL re-check
[ ] Block 169.254.0.0/16 (metadata), loopback, and 0.0.0.0 by default
[ ] Offer strict mode: full RFC1918 and IPv6 ULA private ranges
[ ] Put a deadline and a size cap on every response body read
[ ] Mask secrets before any telemetry or log write
[ ] Require authentication — 91.8% of audited servers do not
默认值得屏蔽的网段包括回环地址(127.0.0.0/8、::1)、链路本地地址(含云元数据的 169.254.0.0/16),以及未指定地址 0.0.0.0。完整的私有网段屏蔽(10.0.0.0/8、172.16.0.0/12、192.168.0.0/16、fc00::/7)应当作为严格模式提供 —— 确实有大量合法部署需要抓取内部预发布主机,而一个谁都无法配置的防护,最终只会被人整个关掉。
CrawlForge 是怎么做的
我们不是站在场外说话。CrawlForge 的 v5.0.0 版本是一项七阶段整改计划,其第一阶段针对的正是这一类缺陷 —— 其中就包括一个我们自己发布过的。
我们的 SSRF 防护会解析主机名并检查得到的地址,但当主机本身已经是 IP 字面量时,就不会执行同样的检查。这正是上面那道阶梯的第三级,而它确实存在于我们的代码中。第 1 阶段的修复方式是:在预检阶段对 IP 字面量主机名执行范围检查,并另外用逐次连接检查包装 undici dispatcher 的 buildConnector,这样即使 Node 从未让它经过 DNS,直接跳向内部地址的重定向也会在建立连接时被拦住。
阶梯上的其余各级覆盖如下:
- IPv4 映射的 IPv6 会在范围检查之前被规范化为内嵌的 IPv4,默认模式和严格模式下都是如此。
- 连接会被固定到已验证的 IP,从而关闭 DNS 重绑定的时间窗口。
- 白名单按每一跳评估 —— 被批准的第一跳不会再让整条重定向链失去防护。
- 浏览器导航被单独防护:
scrape_with_actions在page.goto之前校验,并在之后重新读取page.url(),若导航落入被阻断的网段则关闭页面。 - 所有发起请求的子系统都已接入 —— 页面与元数据抓取、PDF 下载、webhook 投递以及研究通知,都会走防护而非裸
fetch。 - 密钥会被脱敏,然后用量遥测才离开进程。
默认屏蔽回环、链路本地/元数据地址以及 0.0.0.0。SSRF_STRICT=true 会加上完整的 RFC1918 与 ULA 强制策略,ALLOWED_DOMAINS 用于放行可信的内部主机,而 SSRF_PROTECTION_ENABLED=false 则作为 kill switch 存在,供确实需要的人使用。
SSRF_STRICT=true # full private-range enforcement
ALLOWED_DOMAINS=staging.acme.dev # trusted internal targets如果你是使用者而非开发者
你不需要读任何人的源代码,就能显著降低自己的暴露面。
审视你已经接入了什么。 你客户端配置里的每一个 MCP server,都在以你机器的网络访问权限和你的凭据运行。在云上的虚拟机或公司 VPN 中,这个触及范围会比在笔记本上大得多。
优先选择公开自身安全状况的服务器。 一份点名说明自己封堵了哪些 CVE 类别的更新日志,比一张功能列表更有说服力。沉默并不等于安全;从这项研究的数字来看,沉默更接近于相反的证据。
缩小你暴露给模型的攻击面。 如果某个服务器支持工具白名单,就用上它。CrawlForge 接受 CRAWLFORGE_TOOLS 或 CRAWLFORGE_TOOL_GROUPS,只暴露你真正需要的工具,这既能减少上下文膨胀,也能压缩一个被 prompt 注入的模型所能触及的范围。
把抓取到的内容当作敌意输入。 这是这里最重要的一次思维转变。你的 agent 读取的任何页面,都可能包含针对你模型的指令。上述每一种攻击都依赖于这一点,而任何程度的网络加固,都救不了一条把抓取到的文本不加审查就直接送进工具调用的工作流。
正在构建读取实时网页的 agent?免费开始,赠送 1,000 credits —— SSRF 防护默认开启,无需配置。查看完整文档、scrape_with_actions 参考,或了解我们如何应对 stealth 模式下的反爬检测。
亲自试一试——无需注册
在 Playground 中运行 CrawlForge 的 27 个抓取与提取工具中的任意一个,然后免费开始,获取 1,000 credits。
1,000 免费 credits • 一次性 • 无需信用卡
标签
及时获取最新洞察
将教程、产品更新与 Web 抓取技巧直接发送到你的收件箱。
拒绝垃圾邮件,随时可取消订阅。