Skip to content

Security: Mzaxd/dm8-mcp

Security

docs/SECURITY.md

安全说明

本文件说明 mcp-dm8-server 的 SQL 访问控制模型、已知局限,以及部署侧必须落实的纵深防御。

威胁模型

MCP server 把数据库暴露给 LLM,LLM 可能生成任意 SQL。核心目标:execute_query 默认只读;写 / DDL / 会话级破坏只有在管理员显式配置 sqlAccess 级别后才被放行,且无法通过拼接等方式绕过级别限制。

SQL 访问级别(sqlAccess)

自 3.0.0 起,execute_query 的语句权限由所选连接的 sqlAccess 级别决定(readonly/dml/ddl/full,默认 readonly)。级别通过配置显式开启——不配置任何 sqlAccess 时行为与历史版本完全一致(仅 SELECT/SHOW/DESCRIBE/EXPLAIN)。详见 README「SQL 访问级别」一节。

所有级别共同的硬规则:

  • 分号多语句拦截(/;\s*\S/)——即使 full 也不允许一次执行多条语句;
  • Schema 白名单(validateSchemaAccess)对写操作同样生效;
  • WITH(CTE)前缀在 readonly/dml/ddl 拦截(见「固有局限」)。

应用层防护(assertSqlAllowed)

位置:src/utils/validation.ts

  1. 级别化前缀白名单:trim + toUpperCase 后,语句必须以当前级别允许的前缀开头(readonly: SELECT/SHOW/DESCRIBE/EXPLAIN;dml 追加 INSERT/UPDATE/DELETE/MERGE;ddl 追加 CREATE/ALTER/DROP/TRUNCATE;full 不做前缀检查)。
  2. 分号拦截:/;\s*\S/ 禁止分号后跟非空白字符,堵多语句注入。

对照 Datadog 披露的官方 Postgres MCP 漏洞

2025 年 Datadog 安全实验室披露 Anthropic 官方 server-postgres 的只读绕过漏洞(分析原文)。两类攻击向量与本项目的对照:

向量 1:多语句注入 COMMIT; DROP...

官方当初用 BEGIN READ ONLY 事务隔离,被 COMMIT; <写操作> 提前结束事务后注入。

本项目不依赖事务隔离,在字符串层拦截:

Payload 结论
SELECT 1; DROP TABLE x 分号后有 D,拦截 ✅(所有级别)
COMMIT; ... COMMIT 不在白名单前缀,拦截 ✅(full 级别无前缀限制,但分号规则仍拦截多语句形态)
/* */SELECT 1 trim 后首字符 / 不匹配前缀,拦截 ✅
SELECT 1; -- c\nDROP TABLE x 分号后有 -,拦截 ✅

向量 2:会话污染 SET statement_timeout

官方当初 SET 会话变量后连接归还池,污染下一个用户。

本项目:SET 不在 readonly/dml/ddl 白名单前缀,应用层拦截 ✅。但 full 级别会放行 SET——连接池 testOnBorrow 仅做 SELECT 1 探活,不重置会话状态,SET 污染的会话会被下一个借用者继承。这是 full 级别必须仅在受控环境使用的核心原因之一。

固有局限(重要)

assertSqlAllowed 是纯字符串前缀检查,无法防御"以允许前缀开头但语义有副作用"的语句。若 DM8 未来出现 SELECT 开头却可建表/写数据的方言,字符串检查会放行。同理,WITH(CTE)前缀语句无法静态定位主语句动词,在 readonly/dml/ddl 一律拒绝(宁可误拒不可漏放),full 下因无前缀限制而放开。

曾尝试用 connection.getStatementInfo() 做权威只读判定,但 dmdb 1.0.52452 存在 statement handle 泄漏(累积触发 [160]),已回退到字符串检查,详见 validation.ts 注释与 commit f89b22c。

结论:字符串检查是第一道闸,不能是唯一一道。

开启写权限后的部署要求(sqlAccess ≥ dml)

  1. DB 账号权限仍应最小化:开启 dml 的连接,其数据库账号只授予必要表的 DML 权限;开启 ddl 的连接不应使用 SYSDBA。full 级别等于把语句类型控制完全交给 DB 层——务必配合只读/最小权限账号,或仅在一次性/沙箱环境使用。
  2. autocommit 语义:dmdb 驱动默认 autocommit,写语句执行即提交、无法回滚。误操作恢复依赖备份,请为可写的连接环境配置好备份策略。
  3. 审计:所有写操作通过 MCP logging 以 warning 级别上报(含语句、动词、影响行数);启动时 stderr 会列出开启写权限的连接及级别。

Defense-in-depth:DB 账号最小权限(部署必须)

应用层防护之外,必须给 MCP 使用的数据库账号设置只读权限:

  • 创建专用账号,仅 GRANT SELECT 需要查询的表 / 视图;
  • 禁止 INSERT/UPDATE/DELETE 及任何 DDL(CREATE/ALTER/DROP/TRUNCATE);
  • 禁止授予 DBA / RESOURCE 等含写权限的角色;
  • 生产库优先使用只读备库或 DM8 的只读用户。

即使应用层字符串检查被绕过,DB 层权限也会兜底拒绝写操作。这是 Datadog 强调的核心结论:"经典的数据库权限控制对 MCP 同样适用,且是最后一道闸。"

其他安全特性

特性 实现
标识符校验 normalizeIdentifier:/^[A-Za-z_][A-Za-z0-9_]*$/,防 schema/table 参数注入
参数化查询 所有用户输入走绑定参数(:owner、:table),不拼 SQL
Schema 白名单 validateSchemaAccess 限制可访问 schema 范围
凭据 URL 编码 连接串中密码 encodeURIComponent
配置文件忽略 .claude/dm8-mcp.json 已加入 .gitignore
查询超时 见 P1b:连接级 socketTimeout,防止恶意慢查询

报告漏洞

请开 GitHub Issue 或直接提 PR。

There aren't any published security advisories