详细解析AI助手访问数据库时的数据泄露风险,提供列级权限控制、视图隔离等具体防护方案,附MCP协议安全考量。
你把一个 AI 助手接到了生产数据库。你问了一个看似无害的问题——"上周有多少活跃用户注册?"——它愉快地写出了 SQL。很棒。
但这里有个没人会想到的问题,直到它咬你一口:这个助手能读取你连接所能触及的每一列。users.email、customers.phone、payments.card_last4、patients.diagnosis。一旦你给了它运行 SELECT 的能力,你就同时给了它把个人数据拉进聊天窗口、日志文件或 LLM 提供商上下文的能力——而且这往往不是任何人的本意。
这不是拒绝 AI 接触数据的理由,而是要谨慎决定它能看见哪些数据的理由。好消息是:实现这一点所需的工具已经存在于你的数据库中,而且无论查询来自 AI 代理、BI 工具还是初级分析师,模式都是一样的。让我们从头到尾走一遍,从最粗糙的到最干净的。
AI 助手查询数据库时,其权限完全取决于它所使用的凭证。如果它用超级用户或应用主角色的身份连接,它就能看到该角色所能看到的一切。安全研究人员在审查 Model Context Protocol (MCP)——许多工具现在用来连接 AI 和数据库的标准时——反复指出过度授权是这些集成出错最快的途径:连接器暴露的内容超过了任务所需,代理返回的数据远远超出了你希望出现在提示词中的内容。
所以第一个原则虽然无聊但不可或缺:最小权限。AI 应该通过自己专属的、只读的角色来连接,只能访问它真正需要的东西。以下所有内容都建立在这个基础上。
-- A dedicated, read-only role for AI/analytics access
CREATE ROLE ai_reader NOLOGIN;
-- No blanket access to the whole schema
REVOKE ALL ON ALL TABLES IN SCHEMA public FROM ai_reader;
-- Grant only what's needed, table by table
GRANT SELECT ON analytics_events TO ai_reader;
大多数人都知道 GRANT SELECT ON table。但很少有人知道 PostgreSQL(以及 MySQL 8+,语法略有不同)允许你对特定列授予 SELECT 权限。如果一个表混合了公开和敏感字段——大多数表都是这样——这就是你拥有的最锋利的工具。
假设你的 users 表结构为:id、name、email、phone、country、plan、created_at。AI 需要 country、plan 和 created_at 来回答产品问题。它没有理由读取 email 或 phone。
-- Remove table-wide read access
REVOKE SELECT ON users FROM ai_reader;
-- Grant only the safe columns
GRANT SELECT (id, country, plan, created_at) ON users TO ai_reader;
现在,如果 AI 写 SELECT email FROM users,数据库本身会拒绝它并抛出权限错误——在任何 PII 被触及之前。你不需要信任模型、提示词或工具。约束存在于它应该存在的地方:数据库里。
代价是维护工作。随着表增多,追踪谁能查看哪些列变得繁琐。一份简单的列访问矩阵能让你保持清晰。
有时候你不想隐藏整列——只想让 AI 看到掩码版本。对于模型来说,知道一封邮件存在并按其域名分组会很有用,但不需要读取真实地址。这就是视图的优势。
创建一个转换敏感列的视图,撤销对基础表的访问权限,只向 AI 角色授予该视图的访问权限:
CREATE OR REPLACE VIEW users_ai AS
SELECT
id,
country,
plan,
created_at,
-- keep the domain, drop the local part
'***@' || split_part(email, '@', 2) AS email_domain,
-- last 2 digits only, for support triage
'xxx-xxx-' || right(phone, 2) AS phone_masked
FROM users;
REVOKE ALL ON users FROM ai_reader;
GRANT SELECT ON users_ai TO ai_reader;
一个重要的细节:在视图上添加 WITH (security_barrier)。没有它,规划器有时会将 WHERE 子句推到你的掩码表达式下面,从而泄露底层值。屏障强制你的掩码首先运行。
CREATE VIEW users_ai WITH (security_barrier) AS
SELECT ... FROM users;
以下是值得了解的掩码样式:

列控制决定哪些字段;行级安全(RLS)决定哪些行。将它们结合,你就得到了精确的二维访问——AI 看到安全列,并且只看到它被允许的行。
这对于多租户应用最为重要,因为 AI 功能为一个客户提供答案,绝不能暴露另一个客户的记录。
ALTER TABLE orders ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation ON orders
FOR SELECT
TO ai_reader
USING (tenant_id = current_setting('app.tenant_id')::int);
通过会话级设置 tenant ID,同样的 AI 查询只返回该租户的行——无论问题如何措辞。列掩码隐藏了"什么";RLS 隐藏了"谁的"。
数据库授权是最后的防线,但一个设计良好的访问层在 AI 和数据库之间增加了 SQL 本身无法提供的防护栏。MCP 安全文献集中在几个方面:
只读强制执行。直接拒绝 UPDATE、DELETE 和 DDL,这样即使出现错误或被操纵的提示词也无法修改数据。
数据流中的脱敏。扫描工具响应,当 PII 或密钥泄露时只存储脱敏后的表示。
作用域公开。只向 AI 发布批准的表和查询,而不是整个模式。
可审计性。记录 AI 运行的每条查询,使访问可审查和可撤销——而不是分散在提示词和配置文件中的长期凭证中。
这是托管数据库代理存在的重要原因。例如 Draxlr 的 MCP 服务器这样的网关,通过 OAuth 连接,默认只读设计,AI 可以探索模式并运行 SELECT 而无需持有原始凭证或发出写操作。无论使用什么工具,模式才是重点:在模型和数据库之间放置一个可控制的层,并在那里以及数据库本身强制执行 PII 规则。纵深防御优于信任任何单一边界。
把 AI 作为应用主用户连接。最大的错误。给它分配一个专属的、最小的、只读的角色。
在 SELECT 中掩码但保留基础表可读。如果角色仍然可以访问基础表,你的视图就是演戏。撤销基础访问。
忘记在掩码视图上设置 security_barrier。规划器可能通过下推的谓词泄露原始值。设置屏障。
掩码了值但没有掩码 WHERE。AI 写出的查询中 WHERE email = 'jane@acme.com' 即使输出被掩码也能确认特定人员存在。也要限制对敏感列的过滤。
假设"仅内部"意味着安全。进入 LLM 上下文的数据可能在下游被记录或缓存。把每次 AI 查询都视为可能已离开你的系统。
没有审计日志。如果你无法回答"AI 上周读了什么?",你就无法证明合规或发现滥用。
给 AI 访问数据库不是风险所在——给它无限制的访问才是。从最小权限和专属只读角色开始。使用列级 GRANT 完全隐藏敏感字段,在需要形状但不需要值时使用 security_barrier 掩码视图。加入行级安全实现多租户隔离,并在模型和数据库之间放置一个可审计的只读代理,这样规则在两层都得到强制执行。这样做了之后,你的 AI 助手才会真正有用,而且你的安全团队也能签字认可。