我给自己的 MCP 服务器做了一次安全测试:62 个攻击用例,1 个真漏洞

发布于 2026-09-19 23:59 988 字 5 min read

浅唱ヾ落雨殇 avatar

浅唱ヾ落雨殇

落赋韶华 —— 浅唱ヾ落雨殇的个人站点:AI / 理工科 / 开源折腾

先列攻击面 服务器是我自己写的,管着笔记和数据库的读写。我站在攻击者角度想了一遍,列了 6 类 62 个用例: 目录穿越 13 个:想方设法把读取/写入引到笔记目录之外,比如读 /etc/passwd; 命令注入 8 个:往参数里塞 、、 这类 shell 元字符; 超大 payload / DoS 5 个:50MB 单行、万层嵌套 JSON、巨型 id、7 万个端口的列表; 畸形...

先列攻击面

服务器是我自己写的,管着笔记和数据库的读写。我站在攻击者角度想了一遍,列了 6 类 62 个用例:

  • 目录穿越 13 个:想方设法把读取/写入引到笔记目录之外,比如读 /etc/passwd;
  • 命令注入 8 个:往参数里塞 ;、|、$() 这类 shell 元字符;
  • 超大 payload / DoS 5 个:50MB 单行、万层嵌套 JSON、巨型 id、7 万个端口的列表;
  • 畸形 JSON-RPC 16 个:非法 JSON、顶层数组、缺字段、错误版本、不存在的方法;
  • 认证绕过 3 个:跳过握手直接调工具、伪造身份信息、非法协议版本;
  • SQL 注入 17 个:多语句、load_extension、readfile/writefile、递归 CTE 耗尽。

第一轮:61 个挡住了,1 个真被打穿了

打穿的那个很隐蔽,也是最值得说的——前缀目录符号链接逃逸。

我当时的边界检查是这么写的:

str(p).startswith(str(NOTES_DIR))

意思是”这个路径的字符串,是不是以笔记目录开头”。听起来没毛病,但它有个致命问题:字符串前缀不等于目录边界。

/home/admin/notes 是 /home/admin/notes2_evil 的字符串前缀,但 notes2_evil 根本不是 notes 的子目录。于是攻击手法是:在笔记目录里放一个符号链接,指向那个”前缀兄弟目录”里的文件,边界检查通过,越界文件被读了出来。

修复方法也简单,但得改对地方:

  • 边界判断换成 Path.is_relative_to()——逐个路径分量比较,不是比字符串开头;
  • 笔记和数据库路径一律拒绝符号链接,堵死这条逃逸通道。

修完之后把同样的攻击再打一遍,返回”拒绝符号链接”,挡住了。

顺手还加固了四个会崩进程的地方

真漏洞只有那一个,但还有几个”没被打穿但必须修”的:

  • 顶层不是对象的 JSON 会直接把进程干崩。[1,2,3] 能过 json.loads,但后面 msg.get() 抛错,异常处理里再调一次又抛,进程就没了。修法是校验 isinstance(msg, dict);
  • 一万层嵌套的 JSON 也会崩。它抛的是 RecursionError 不是 JSONDecodeError,我原来的 except 没覆盖到;
  • 单行 50MB 直接整行吸进内存。加了 1MB 的行上限;
  • 读笔记没有大小上限,超大文件会把响应撑爆。加了 2MB 上限。

第二轮回归:62 个全挡住

修完之后把 62 个攻击全部重新打一遍:

  • 三个会崩进程的攻击全活下来,进程不崩,按规范回错或直接丢弃;
  • 命令注入 8 个全挡——因为我所有系统命令都走 subprocess.run(list),没有 shell=True,天然没有注入面;
  • SQL 注入 17 个全挡——authorizer 白名单 + 只读模式 + 3 秒超时 + 行/字节上限,递归 CTE 耗尽被超时掐断;
  • 目录穿越 13 个全挡——URL 编码、反斜杠、Unicode 全角、混合路径、绝对路径全被拦。

顺带还发现了一个事

用手写 HTTP 请求探的时候发现,家里一个 Web 控制台监听在 0.0.0.0(所有网卡),局域网 IP 直接访问返回 200,而且只读 API 没带任何认证就吐出了 17KB 的会话元数据。配置文件里其实配了 token,但那组 API 没强制校验。

这个不算漏洞(监听地址是内网段),但值得记下来:监听在 0.0.0.0 和 127.0.0.1 是两回事,配了认证和强制校验认证也是两回事。

收进来的几句话

这次演练最大的收获不是”找到 1 个漏洞”,而是验证了那句话:安全不是”我觉得”,是”我打过了”。

攻击面清单不是抄出来的,是站在攻击者角度一条条想出来的;修复也不是改完就完,是修完立刻把同样的攻击再打一遍确认挡住。

自己的门,得自己先踹几脚。

© 2025 - 2026 浅唱ヾ落雨殇
Powered by theme astro-koharu · Inspired by Shoka