为AI数据库访问设置只读与脱敏边界
文章讨论通过 MCP 将 Claude Desktop、Cursor 接入 PostgreSQL,并以只读 AST 校验和个人信息脱敏作为防护方向。重点风险包括误执行破坏性 SQL、无约束全表扫描,以及敏感记录进入模型上下文。
AI 深度解读
让 Claude Desktop 或 Cursor 查询 PostgreSQL,确实能省掉来回贴表结构、查外键、补 SQL 的功夫。但一旦接入真实数据库,误删数据、查询拖垮实例、客户信息进入模型上下文,就成了同一条链路上的风险。
作者给出的方案是 PgSafe MCP:在模型与数据库之间加入 SQL 语法树检查、查询成本限制和敏感信息遮罩。它的价值在于把限制落实到执行链路;不过,原文没有测试报告,离「接上就安全」还有不少需要核实的细节。
只给 SELECT 权限,为什么还要检查 SQL
作者首先强调数据库账号应当只读,以 GRANT SELECT 作为基础。但账号可能仍有存储过程的执行权限,开发者也可能误用权限更高的环境凭据,因此还需要独立的查询检查。
PgSafe MCP 使用 Python 的 sqlglot,把 SQL 解析成抽象语法树(AST)。大白话理解,就是先看懂语句由哪些操作组成,再决定是否放行,而不只是搜索几个危险单词。
按原文描述,它会阻止三类操作:
- 修改结构的
DROP、ALTER、CREATE、RENAME。 - 修改数据的
INSERT、UPDATE、DELETE、TRUNCATE。 - 管理操作
VACUUM、REINDEX、GRANT、REVOKE。
允许通过的只有纯 SELECT 和 EXPLAIN 查询树。对于 SELECT 1; DROP TABLE users; 这样的多语句输入,它逐条解析,只要发现不允许的节点,就拒绝整个批次。
数据库权限和 AST 检查应该叠加使用。
这里仍有一个空白:原文没有说明如何处理 SELECT 中的函数调用,以及可能产生副作用的函数。允许某种语句类型,不等于已经证明它的所有执行路径都只读。
查询不改数据,也可能拖垮数据库
第二层处理的是资源消耗。一个没有 LIMIT 的 SELECT * FROM audit_logs,遇到数千万行日志时,即使不修改任何记录,也可能造成大量读取和结果传输。
PgSafe 会在执行前运行 EXPLAIN (FORMAT JSON),读取查询计划的估算成本,再与阈值比较。默认上限是 50,000 cost units。原文展示的拒绝信息如下:
Query rejected: Estimated plan cost (482190.50) exceeds maximum allowable ceiling (50000.00).
Suggestion: Add a WHERE clause on indexed columns or specify an explicit LIMIT.
要看的是第一行:482190.50 超过了 50000.00,因此查询被拦截。第二行则给模型一个修改方向,让它尝试补过滤条件或限制返回数量。
这个设计比只返回「执行失败」更有用,模型能够根据具体原因调整 SQL。但 planner cost 是优化器的估算值,不是毫秒数,也不是 IOPS 配额。原文没有给出阈值的校准方法、误拦截比例或实际负载对照。
因此,不能从这个例子推导出「低于 50,000 就不会影响生产」。查询超时、返回行数限制和并发控制是否存在,原文也没有交代。
接入很短,数据边界却要逐项确认
第三层是在查询返回后检查列名和单元格内容,通过正则模式处理敏感数据:
- 邮箱
johann@example.com变成j***n@example.com。 - 电话及信用卡模式被遮罩,例如
***-****-1234。 password_hash、api_key、secret、ssn等敏感列的值替换为[REDACTED_SECRET]。
它能减少原始数据直接进入模型上下文的机会,但邮箱仍保留域名和部分字符,号码仍可能保留末四位。这属于遮罩,不能据此认定数据已无法识别个人。别名、JSON 内嵌字段、特殊格式是否覆盖,原文没有说明。
接入方面,作者使用 uvx 启动服务,无须手动克隆仓库或管理虚拟环境。Cursor 在项目根目录添加 .cursor/mcp.json:
{
"mcpServers": {
"pgsafe-mcp": {
"command": "uvx",
"args": [
"--from",
"git+https://github.com/johannschiffer/pgsafe-mcp",
"pgsafe-mcp"
],
"env": {
"PG_HOST": "127.0.0.1",
"PG_PORT": "5432",
"PG_DATABASE": "app_db",
"PG_USER": "readonly_analyst",
"PG_PASSWORD": "your_secure_password"
}
}
}
}
这里要看 PG_USER 和 PG_PASSWORD:账号名称叫 readonly_analyst,并不能证明数据库真的授予了只读权限;示例中的密码位置也需要替换,项目配置不应带着真实凭据提交。
Claude Desktop 使用相同的 mcpServers 结构。原文给出的配置路径是 macOS/Linux 下的 ~/.config/Claude/claude_desktop_config.json,以及 Windows 下的 %APPDATA%/Claude/claude_desktop_config.json,没有提供客户端版本或路径验证过程。其示例连接 localhost:5432,数据库名为 production_read_replica。
重启 Claude Desktop 或重新加载 Cursor 窗口后,按原文描述会出现 pgsafe-mcp,提供 list_tables、describe_table、execute_safe_query 三个工具。
我的判断
这个方案说得对的地方,是把三种风险分开处理:权限和 AST 管操作范围,成本检查管查询负载,结果遮罩管数据暴露。相比在提示词里要求模型「不要改库」,执行前拒绝更有约束力;相比单独使用只读账号,它又补上了负载与结果内容这两块。
但原文更接近产品配置说明。它没有提供攻击样例覆盖率、脱敏准确率、额外延迟,也没有展示 Claude Desktop 或 Cursor 的完整实测记录。文中的错误信息是示例,不能当作性能测试结果。
作者还推广了配置生成器、包含 25+ 份 .cursorrules 和客户端模板的 Genesis Starter Kit(Free / $0+),以及 $19 USD 的 Genesis Enterprise Suite。后者提到零停机迁移验证和企业安全中间件,但没有展开实现,不能把这些宣传项算作 PgSafe 已验证的能力。项目声明采用 MIT 许可证,也不等于安全性经过审计。
能拿来做什么
- 在前端或 Node 项目中,先让助手通过
list_tables、describe_table核对表结构和字段关系,再编写查询或迁移方案;数据库侧使用实际受限的只读账号。 - 在测试环境准备正常查询、危险语句和多语句输入,检查
execute_safe_query是否按描述拒绝,同时验证函数调用等原文未交代的边界。 - 用自己业务的典型 SQL 检查默认 50,000 成本阈值,记录正常查询是否被拦截,并确认超时、行数和并发是否另有限制。
- 用虚构邮箱、电话、令牌和嵌套数据检查返回结果,再决定哪些表能开放给助手;不要拿真实客户数据做首次脱敏验证。