db-mcp 是一个用 Go 实现的只读数据库 MCP Server,支持 MySQL、PostgreSQL、SQLite、SQL Server、TiDB、GaussDB 和 ClickHouse。它允许 SELECT、SHOW、DESCRIBE、EXPLAIN 与 USE,阻止数据修改、DDL、权限控制、事务控制、文件操作和若干危险函数,并提供返回行数与查询超时限制。客户端可以通过 stdio 在本机启动,也能通过 Streamable HTTP 远程访问。
它的安全模型主要依赖 SQL 文本验证:移除注释、阻止多语句、检查禁止关键词和危险函数,再把通过的查询发送给所选数据库驱动。这种方案能挡住许多常见误操作,但跨七种数据库方言时容易遇到解析边界。生产部署必须再使用数据库原生只读账号、网络隔离和资源限制,不能把字符串过滤当作唯一安全保证。
项目列出的驱动包括 mysql、postgres、sqlite、sqlserver、tidb、gaussdb 与 clickhouse。MySQL 与 TiDB 使用相近 DSN,PostgreSQL 与 GaussDB 使用键值连接参数,SQL Server 和 ClickHouse 使用各自 URL,SQLite 使用文件或内存数据库。
“支持同一查询接口”不等于七种数据库拥有完全相同的 SQL 语义。注释、字符串转义、函数、事务、EXPLAIN 和行数限制语法都有差异。
每个生产驱动都应独立测试。不能只验证 MySQL 后就认定 PostgreSQL、SQL Server 与 ClickHouse 的过滤同样可靠。
list_tables 列出表,describe_table 返回表结构,execute_query 执行只读 SQL,get_table_sample 获取少量样本,get_table_count 统计行数。
工具表面较小,便于模型选择,也降低管理命令被误用的概率。样本和计数工具可用固定查询实现,比让模型每次自由生成 SQL 更容易审计。
表名参数仍属于不可信输入。实现应根据目标方言正确引用标识符,或只接受从 list_tables 返回的真实名称,避免把表名直接拼入查询。
execute_query 的允许类型包括 SELECT、SHOW、DESCRIBE、EXPLAIN 与 USE。验证器通常先清理空白与注释,再检查首个有效关键词。
允许列表比只维护危险词列表更保守,因为未知语句默认拒绝。不过 WITH CTE、方言前缀、会话指令和嵌套结构会让“第一个词”判断变复杂。
USE 会切换当前数据库,在连接账号可访问多个库时可能越过初始 DSN 的边界。生产只读服务应考虑禁用 USE,或将可切换库限制在明确清单。
项目列出的数据修改关键词包括 INSERT、UPDATE、DELETE、REPLACE 与 MERGE。它们覆盖常见关系数据库写入动作。
危险语义不一定出现在顶层。数据修改 CTE、触发器、带副作用函数和存储过程都可能隐藏写入。某些数据库还支持 SELECT INTO 等看似读取的写入形式。
因此连接账号应没有写权限。即使验证器遗漏某种新语法,数据库授权仍能拒绝。
CREATE、DROP、ALTER、TRUNCATE 和 RENAME 被归为结构修改;GRANT 与 REVOKE 被归为权限控制;这些操作默认阻止。
数据库方言还可能提供 COMMENT、VACUUM、ANALYZE、ATTACH、DETACH、OPTIMIZE、SYSTEM 等具有状态或资源影响的命令。验证规则需要针对每种驱动补全。
最小权限账号不拥有建表、改表、授权、扩展管理和数据库所有者能力,能够为不完整关键词表提供兜底。
BEGIN、COMMIT、ROLLBACK 与 SAVEPOINT 被阻止,避免 Agent 改变服务预设的事务边界或持有长事务。
如果客户端能自行 COMMIT,它可能提交服务原本打算回滚的变化;长事务还会持锁、阻碍垃圾回收或造成版本膨胀。
服务应由自身创建、超时和结束事务,连接归还池前清理会话状态。只拦关键词而不管理真实事务生命周期仍不够。
MySQL 的 INTO OUTFILE、INTO DUMPFILE 和 LOAD_FILE 可以把数据库能力延伸到服务器文件系统。项目明确阻止这些模式。
其他数据库有 COPY、BULK、外部表、目录对象和 URL 表函数等类似能力。跨数据库实现不能只使用 MySQL 关键词。
数据库账号不应拥有 FILE、外部数据源或目录访问权限,数据库服务器进程也应受操作系统与容器隔离。
项目列出的危险函数包含 SLEEP、BENCHMARK 和 LOAD_FILE。SLEEP 可长时间占用连接,BENCHMARK 可消耗 CPU,LOAD_FILE 可读取服务器文件。
函数名检查能阻止直接调用,却不能证明所有其他函数无副作用。数据库扩展、自定义函数和高权限存储函数可能访问网络、文件或修改数据。
函数黑名单必须配合 EXECUTE 权限白名单。对 Agent 账号撤销不需要的过程与函数执行权限,尤其是以定义者身份运行的对象。
项目说明自动移除双横线和块注释。这样可以防止攻击者用注释切断关键词或把危险语句藏在前导文本之后。
不同方言还支持井号注释、嵌套注释、版本条件注释或特殊提示。字符串与引用标识符内部的注释符号不能被删除,否则会改变查询含义。
可靠实现需要词法状态机或真正的方言解析器,跟踪普通文本、字符串、标识符和注释状态。简单正则替换容易误判。
验证器会检查分号并阻止多语句执行,避免在合法 SELECT 后追加 DELETE 或 DROP。
分号也可能位于字符串、过程文本或注释中,不能直接把出现分号等同于多语句。各驱动是否默认允许多个 statement 也不同。
最安全的驱动配置是关闭 multiStatements,并使用只允许单语句的协议路径。应用检查用于给出更清楚的错误,而驱动和数据库负责最终限制。
文档提到检测可疑模式,但没有把它描述为完整形式化验证。安全文章应避免把模糊规则理解成所有 SQL 注入都能被阻止。
跨方言 SQL 的语法空间很大,编码、转义、内置函数和版本升级都会改变边界。未知或解析失败的语句应默认拒绝。
每次新增驱动或升级数据库版本,都要建立恶意样本与合法复杂查询回归集,观察误判和漏判。
max-results 默认一千,服务会自动添加 LIMIT 以限制结果集。它保护 MCP 响应体、内存和模型上下文。
并非所有数据库都使用相同 LIMIT 语法。SQL Server 常用 TOP 或 OFFSET FETCH,某些查询尾部已有锁定或格式子句,机械拼接可能造成语法错误。
行数限制也不限制扫描成本。COUNT、排序、聚合或大 JOIN 可能只返回一行,却读取海量数据。
timeout 默认三十秒,用来中断慢查询和意外资源消耗。不同驱动对超时的实现可能是客户端取消、上下文截止或服务器会话设置。
必须验证超时后数据库端语句真的停止,连接没有留在活动状态,事务得到清理。客户端停止等待但服务器继续运行不算有效保护。
还需要连接池、并发和数据库资源治理。大量五秒查询依然可能耗尽 CPU 与 IO。
stdio 适合本机桌面与编码客户端,服务进程由客户端启动,协议只在标准输入输出传输。
诊断日志必须写 stderr,不能污染 JSON-RPC stdout。配置中的 DSN 不应出现在命令输出或崩溃日志。
本地模式的访问边界通常继承操作系统用户。限制二进制、配置和 SQLite 文件权限,防止工作区脚本替换可执行文件。
HTTP 模式默认示例八零八零端口并提供 MCP 端点。远程传输扩大攻击面,必须增加认证、TLS、来源限制与反向代理。
不要直接把端口暴露到公网。项目的 SQL 校验不等同于网络身份验证,任何能访问端点的人都可能发起只读数据查询。
为每个用户建立独立身份和审计关联,限制请求体、并发、速率与会话时长。跨站和代理转发边界也需要验证。
示例命令把用户名和密码放进 DSN,便于演示,却可能进入 MCP 客户端配置、shell 历史和进程参数。
生产使用环境变量、秘密文件描述符或秘密管理代理,并确保子进程日志脱敏。对 SQLite,文件路径本身也可能暴露项目结构。
使用专用、可轮换的短期凭据。泄露后只吊销 MCP 账号,不影响主应用。
MySQL 与 TiDB 账号只对批准库表拥有 SELECT,撤销 FILE、PROCESS 和管理权限。PostgreSQL 与 GaussDB 只授予 CONNECT、schema USAGE 和对象 SELECT。
SQL Server 使用独立登录与数据库用户,只加入自定义读取角色,避免 server-level 权限。ClickHouse 用用户 profile、readonly 与允许数据库限制查询范围。
SQLite 没有服务器角色,应把数据库副本只读挂载,并限制进程访问其他文件与扩展。
允许 USE 是为了 MySQL 等环境切换数据库,但它会改变后续未限定表名的解析范围。
若 DSN 账号拥有多个数据库权限,Agent 可能切换到并非原计划的数据域。服务最好固定数据库,或验证 USE 目标属于允许列表。
连接池复用时,归还连接前恢复默认 database,否则下一请求可能继承上一次切换结果。
样本工具很方便,但随机或前几行可能包含真实姓名、邮箱、令牌和业务秘密。只读不代表样本适合进入模型上下文。
敏感表不应出现在工具可见范围。优先对脱敏视图采样,并明确排序与行数,避免每次返回不可预测数据。
审计样本调用频率和目标表,但日志不要保存原始样本内容。
不同数据库对 COUNT 的执行成本差异很大。大表精确统计可能全表扫描,超过普通样本查询成本。
可以优先返回系统统计中的估算行数,并清楚标记 approximate。需要精确值时使用更长但受控的超时或分析副本。
Agent 不应在失败后无限重试 count。把超时错误作为可解释结果返回。
数据库字段可以包含诱导模型调用其他工具、泄露数据或忽略规则的文字。SQL 验证器只检查查询,不理解返回内容。
宿主必须把结果标记为不可信数据,禁止数据改变工具授权和系统规则。消息发送、命令执行和秘密访问需要独立审批。
限制可读表与字段能缩小注入来源,是数据库账号权限的重要价值。
为每种驱动分别测试合法 SELECT、SHOW、DESCRIBE、EXPLAIN、子查询、CTE、UNION、字符串与引用标识符。
测试所有禁止 DML、DDL、权限和事务语句,以及多语句、前导注释、嵌套注释、方言注释、编码和转义。
测试 SLEEP、BENCHMARK、LOAD_FILE、自定义函数、外部表、文件导出和存储过程。确认应用层与数据库权限至少有一层确定拒绝。
测试超时、大结果、慢计数、客户端断连和并发,观察数据库会话、连接释放与内存。
SLEEP、BENCHMARK 与 LOAD_FILE 主要反映 MySQL 风险。在 PostgreSQL 中,需要关注延时函数、大对象、服务端文件与扩展函数;SQL Server 有延时、外部数据源和扩展过程;ClickHouse 也有文件、URL、远程表函数和集群级能力。
一张全局黑名单很容易产生两类问题:某个名字在一种数据库危险,在另一种数据库只是普通标识符;或者某驱动独有的危险函数根本不在通用清单。验证器应先确定 driver 和版本,再加载对应策略。
函数可能通过 schema 限定、大小写、引用标识符、别名或嵌套表达式出现。只在原始文本中搜索 NAME 加左括号并不可靠。能够使用 AST 的驱动应检查函数调用节点,无法解析时采取失败关闭。
自定义函数无法靠内置清单发现。部署流程要从数据库系统目录导出 Agent 账号实际可执行的函数与过程,逐项评审,并在新增对象时触发权限复核。
统一 MCP 接口应把错误分成策略拒绝、语法错误、权限不足、资源超时、连接故障与结果截断。模型据此才能决定修改查询、缩小范围或请求用户处理。
不能把所有数据库错误都包装成“查询不安全”。权限拒绝可能说明账号边界正常,语法错误可能来自方言差异,超时则要求优化查询;混在一起会诱导 Agent 反复尝试绕过。
返回给客户端的错误应包含稳定代码、driver、受控摘要和可执行建议,但去掉 DSN、密码、服务器路径、内部堆栈和完整敏感 SQL。服务端日志可保留关联标识,供管理员查看详细原因。
Agent 重试策略也按错误类型区分。语法与策略拒绝不自动重试,网络瞬断可以有限退避重试,超时应先缩小查询。这样既减少数据库压力,也让安全告警更有意义。
给已有 LIMIT、分页、UNION、CTE、子查询和尾部注释的 SQL 分别测试自动限制。服务必须限制最终响应,而不是误改内部子查询的语义。
对于无法安全改写的方言语句,可以使用驱动级流式读取,在达到 max-results 后停止,而不是冒险拼接文本。此时还要确认数据库端游标关闭并取消剩余工作。
响应应明确返回 truncated 状态。模型如果不知道结果被截断,可能把部分数据错误地概括为完整结论。
确认选择了正确 driver,当前版本和方言已经单独测试,不把一套关键词规则视为跨库证明。
确认 DSN 使用独立最小权限账号,秘密不在命令行、仓库与日志中,SQLite 文件采用只读副本。
确认 HTTP 模式有 TLS、认证、网络限制、限流和审计;不需要远程访问时只使用 stdio。
确认 USE、函数执行、外部文件、系统目录和样本工具的权限边界,敏感数据通过视图或列权限排除。
确认 max-results、timeout、连接池和并发适合实例容量,超时会真正取消服务器查询并清理事务。
db-mcp 用一个统一工具层连接七类数据库,以允许操作、禁止关键词、多语句阻断、危险函数检查、行数和超时形成基础防护。它最适合充当纵深防御中的应用层:提前拒绝明显危险输入并提供一致接口。真正的安全底座仍是每种数据库自己的只读账号、对象授权、驱动设置和资源治理。只有按方言逐一验证,才能让“多数据库支持”同时具备可信的只读边界。