你电脑上那些"只给自己用"的本地服务,任意网页都能调

你电脑上那些

一、先搞清楚"来源"是什么

浏览器判断两个页面是不是"自己人",看的是来源(origin),由三部分组成:协议、域名、端口。https://example.com 和 http://127.0.0.1:8765 是两个完全不同的来源,哪怕它们跑在同一台机器上。

这套规则叫同源策略。页面脚本发非 GET 请求时,浏览器会自动加一个 Origin 头,标注请求来自哪个页面,脚本自己改不了。跨来源时,浏览器会先用一次"预检"请求问服务器同不同意,服务器不回应,真正的请求就发不出去,页面也读不到结果。

这套机制保护的是"跨来源",不是"跨机器"。同源策略从来不管你的服务器是公网还是 127.0.0.1。

二、DNS 重绑定:把"跨来源"变成"同来源"

DNS 相当于互联网的电话簿,把域名翻译成 IP。域名归谁所有,谁就能改它的解析记录,而每条记录都带一个有效期(TTL),决定浏览器能记多久。

攻击者的做法,就是把自己的解析记录换掉:

  1. 你点开一个链接或广告,页面来自攻击者控制的域名,解析到的也是他的服务器,TTL 设得很短,比如几秒;
  2. 页面里的脚本什么都不做,就等这条解析过期;
  3. 过期后,攻击者的 DNS 把同一个域名重新指向 127.0.0.1,也就是你自己的电脑;
  4. 脚本再发一次请求,浏览器重新解析,连上了本机那个监听 8765 端口的服务;
  5. 页面和请求现在共享同一个来源,预检没了,脚本可以直接读回结果,屏幕上什么都不会显示。

重绑定能改的是"这个名字解析到哪个 IP",改不了"浏览器在这个名字里写了什么"。这就是它唯一的破绽,也是唯一的解法。

三、为什么偏偏是 MCP 服务器危险

普通本地服务大多没什么可偷的。MCP 服务器不一样,它存在的意义就是"让发请求的一方去执行动作",手里还往往握着执行动作需要的东西:文件访问权限、数据库连接、API 令牌。而且本地部署的版本普遍不做身份验证。

文章给出的判断口径很实用——如果你的服务走 HTTP(stdio 方式不受影响),且满足下面任意一条,就该当成紧急问题处理:

情形风险点
跑在开发机上,哪怕"只是测试用"开发机权限最高,文件、密钥都在
跑在公司内网、VPC 或 K8s 集群里,员工浏览器够得着内网横向暴露面大
打包进桌面应用分发每个用户本机都开着一个口子
用浏览器 Cookie 认证同时构成典型的跨站请求伪造场景

反过来,完全公开、不需要任何凭据的服务器风险相对低——攻击者能拿到的,用 curl 本来也能拿到。但文章的建议仍然是照修不误:规范要求所有服务器都做这个检查,成本只有几行代码;而且没做检查的代码,通常也没在上线版、测试集群、内网副本里做过。

四、防御只有一件事:校验两个请求头

重绑定的请求有一个共同特征:请求到达时,Host 头写的是攻击者的域名,Origin 头写的也是攻击者的页面来源,而不是 127.0.0.1 或 localhost。服务器只要拒绝"Host / Origin 与预期不符"的请求,整条链路就断了。

自查方式也很直接:用官方给出的检测工具跑一遍你的服务地址,看 security_origin_validation 这一项是否通过;不通过就按对应 SDK 的补丁改,改完再跑一次,并把这个检查放进 CI,防止以后被改回去。

这件事的启发不止于 MCP:任何"只监听本机所以不用密码"的自建服务——本地文档工具、家庭 NAS 的管理后台、开发用的数据库面板——都建立在同一个错误假设上。加上来源校验,或者干脆加一层口令,成本都很低。