ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

给 Agent 接数据库:账号密码与 API Key 的配置、授权和排查

给 Agent 接数据库:账号密码与 API Key 的配置、授权和排查

做一个能查订单的 Agent,最麻烦的往往不是把自然语言转成 SQL,而是一旦数据库账号交给工具,怎样才能不让用户查到不该看的订单。开发到一半你会遇到几个具体问题:模型服务 Key 和数据库密码是不是同一种密钥?工具参数里要不要带上租户 ID?连不上数据库时先看密码还是先看认证方式?出问题后怎么从日志里把业务用户和数据库会话对应起来?

这些问题如果只停留在“数据库密码到底放哪个环境变量”的层面,很容易把安全边界放错位置。需要先区分三类凭据,再分别看注入、授权、审计和故障排查。

先把三类凭据分开

模型服务 API Key、工具访问凭据和数据库登录凭据看起来都是一串字符,但授权边界不同。模型服务 API Key 只用于服务端请求模型推理,证明这个调用方有权请求模型;它不应该被拿去连接订单库,也不应该进入模型上下文。数据库账号代表某个服务身份可以访问某些表,也不应该由模型直接持有。统一存储这些密钥可以,但按用途分别授权和分发不能合并。

下面这张表概括了三类凭据的典型位置。

类型用途典型持有方不能做什么
模型服务 API Key调用大模型推理服务服务端配置或密钥管理不用于数据库连接或业务 API 身份
工具访问凭据访问内部业务 API、MCP 工具或下游服务受控工具执行服务不直接交给模型或写进提示词
数据库登录凭据验证数据库连接身份;可访问范围由账号权限决定受控查询服务或后端服务密码等认证秘密不进入模型上下文、工具返回、日志

在这个链路里,凭据的注入位置是固定的:模型服务 Key 只在服务端编排器请求模型时使用,数据库账号只在查询工具访问订单库时使用。下面的图给出了大致流向。

MCP 官方安全实践里有一条反模式:在远程授权链路中,MCP 服务器如果接受并未明确签发给自己的令牌,不验证受众,再原样透传给下游,就可能扩大授权范围。这只针对远程授权链路中的令牌透传,不是说所有 MCP 服务器都使用同一套 OAuth 流程,也不等于任何代理都禁止所有令牌传递。正确做法是服务器验证令牌的受众等必要声明,并为下游取得适当授权。同样一串字符,不代表能拿去开每道门。

把密钥放在哪个框里

一个查订单的 Agent,大致有四个环节:业务用户、Agent 编排与模型、受控查询工具、业务 API 或数据库。密钥只应注入到真正执行访问的那一层。模型服务 API Key 由服务端编排器用于请求模型推理接口,不进入模型上下文;模型只返回工具调用意图,由编排器派发。工具服务从服务端可信上下文获得业务用户身份,并读取数据库账号访问数据库。数据库账号不直接暴露给模型或用户。

环境变量经常被当成安全边界,但它只是交付途径,不等于保密隔离。一个在同进程内有任意代码执行能力、或能进入同一终端环境的 Agent,可能读取环境变量或挂载的配置文件。例如查询工具运行在容器里,数据库密码以 Secret 文件挂载,能以服务进程身份执行代码的人,也可能读取这份文件。因此应限制该服务能够取得的凭据、数据库权限和网络访问范围。业务代码中的租户过滤能约束正常工具调用,但若服务进程已被控制,不能指望攻击者仍遵守这段代码;还需要数据库侧的有效限权与隔离。小规模本地实验可以用受限配置文件或环境注入起步;生产环境则应核验凭据能否撤销、权限是否足够小、访问能否追踪。

工具只暴露订单状态

以订单查询场景为例,受控查询工具可以只接收一个order_id:

{"tool":"query_order_status","parameters":{"order_id":"ORD20260101-1234"}}

模型只需给出这样的调用参数。服务端在收到该参数后,结合已认证上下文补入客户标识和租户标识,不需要模型知道这些内部字段。不要让模型传入tenant_id或customer_id,也不要把数据库账号、密码或内部 API Key 放进工具参数或工具返回中。

这样设计的原因很直接:模型不需要知道数据在哪个库、由哪个账号读取,它只需要表达“用户想查这个订单状态”。真正决定能不能查的是服务端上下文。

身份从服务端来,参数化只是第一步

SQL 参数化可以防止注入,但不能代替授权。即使order_id是参数绑定,如果只按order_id查询,攻击者或模型幻觉仍可能传入一个不属于当前用户的合法订单号。在同一个租户内,还要区分不同客户身份,否则同租户不同用户之间仍可能越权。

下面以 PostgreSQL 参数绑定写法举例,订单查询可以写成:

SELECTorder_id,status,updated_atFROMordersWHEREtenant_id=$1ANDcustomer_id=$2ANDorder_id=$3;

$1、$2来自服务端已认证上下文,分别对应租户标识和客户标识;$3来自模型工具参数中的order_id。参数绑定用于防止值被解释为 SQL,租户与客户条件共同限定当前用户可以读取的订单。客户之间要防止越权,租户之间同样要防止越权,所以这两层条件不能只留一个。

如果业务上存在客服代查,应走单独的业务授权和审计路径,而不是默认让同租户所有登录用户都能查看该租户下全部订单。

PostgreSQL 的 Row Security Policies 可以限行,但要注意 superuser、BYPASSRLS 角色以及表所有者通常绕过 RLS;FORCE ROW LEVEL SECURITY只处理表所有者绕过,不处理前两类。查询服务不应使用这些角色。共享数据库账号只靠current_user无法区分业务用户;如果让模型自己填tenant_id或customer_id,或自己执行SET任意租户变量,只是把身份变成模型可改的输入,不构成可信隔离。只读权限也不意味着没有风险:只读账号仍可能读取其他租户数据,或对全表做大量扫描。

数据库权限小到刚好够用

给 Agent 的数据库账号不应是管理员账号,也不应直接使用内置系统账号。对于订单查询工具,通常采用只读账号,授权查询状态相关的视图或存储过程。视图通常授予SELECT权限,存储过程则授予EXECUTE权限,而不是笼统授一个读权限。只读工具场景不代表所有 Agent 永远只能读;遇到写任务时,应使用独立的受限写工具,明确对象、动作和授权,是否引入人工审批按风险决定。本文仍然聚焦查订单。

最小权限还需要考虑连接来源、最大连接数、语句超时。订单状态查询应建立合适索引,并检查执行计划。如果订单状态还需要关联支付或物流表,最好通过视图或存储过程把允许的连接关系固定下来,避免直接开放跨表查询权限。限制输出行数可以控制返回的数据量,但不保证降低扫描、排序或聚合成本。单订单查询唯一约束要能保证一个订单号不会意外匹配多条记录。支持动态短期凭据时,使用短期数据库账号,到期自动失效。长期密钥不要传入模型上下文、工具返回或日志。

即使使用视图或存储过程,也要核验其底层策略、security definer以及身份传递方式,不能默认它们自带租户隔离。

连不上时:从连接、认证到存储格式

业务账号认证失败时,管理员账号正常登录不能证明业务账号正常。我们社区的《内置账号可正常登录,手动新建用户全部登录失败》记录过一则 KingbaseES 案例:内置system用户可以正常登录,手工新建user01等用户全部提示password authentication failed。

排查时发现,sys_hba.conf要求使用scram-sha-256认证,但执行:

SHOWpassword_encryption;

结果为md5。内置账号存储的是 SCRAM 校验信息,而新建账号按md5格式存储密码,认证要求与存储格式不匹配,所以新建用户无法通过认证。修复方式是统一将password_encryption改为scram-sha-256,重载并核验,然后重置受影响用户密码,使其生成相应存储格式,复测通过。修改参数本身不会把已存储旧密码自动转为新格式。

这类问题可以按从外到内的顺序排查:先确认数据库服务端口和网络可达,排除连接层问题;再确认客户端使用的数据库名、用户名、密码与连接串一致;接着查看服务端认证配置与password_encryption参数是否匹配;然后由有权人员核对用户实际存储的密码格式是否符合认证要求,但不导出完整校验串,确认不匹配才重置;最后重置业务账号密码并复测,观察新连接是否成功。

![业务账号认证失败的排查][fig3]

排查时不应改用管理员账号绕过,否则业务权限会被放大,真实问题也被掩盖。

轮换、撤销与留痕

轮换数据库密码后,需要同步更新查询服务的配置,让新连接使用新凭据。已有连接和连接池中的旧会话可能继续存在,因此还要按业务影响安排连接池重建或受控断开。对于 API Key,则要核验调用方已切换新密钥、旧密钥已在服务端撤销,以及新请求是否按预期被接受或拒绝。

撤销数据库权限时要区分认证与授权。REVOKE SELECT撤销的是对象权限,账号仍可能登录;确认没有角色继承、PUBLIC等其他授权来源后,应检查后续查询是否被拒绝。已经开始的语句和事务如何处理,需要按数据库实际行为验证。禁用LOGIN或更换密码主要约束新认证,不自动终止已有连接;撤销有效的CONNECT权限也不会自动断开现有会话。因此,验收应分别检查新连接、池内旧连接的后续查询,以及已经开始的操作,必要时受控终止会话。

日志应包含业务用户、工具名、目标资源、授权结果、请求 ID、数据库角色、耗时或结果行数等元数据。不要把 token、密码、完整敏感结果写入日志。服务层记录业务用户和请求 ID,数据库层记录数据库角色和 SQL 指纹,二者通过请求标识关联,才能形成从业务操作到数据库执行的追踪链。共享数据库账号的数据库日志不能自动识别业务用户,必须依靠服务层上下文和关联标识。

验收表

下面用一张表检查订单查询 Agent 的基本要求。

操作应该检查的结果
检查查询服务账号的数据库权限只能访问指定对象,无无关表读取或建表等权限;行范围另由业务授权或行级策略验证
查看工具输入参数只有order_id等业务参数;tenant_id、customer_id来自服务端上下文,模型不可填
分别查询同租户其他用户、其他租户的订单号返回空或拒绝,不返回他人订单状态
撤销 SELECT 权限后,用旧会话查询依据数据库行为,查询应被拒绝;必要时终止旧会话
禁用 LOGIN 或轮换密码后发起新连接新连接被拒绝或旧凭据失败
轮换数据库密码并重建连接新连接使用新密码成功,旧凭据新连接失败
查看服务端与数据库日志能看到业务用户、请求 ID、数据库角色和授权结果,且无 token、密码或完整订单结果

这些措施回答的是 Agent 接数据库时最常见的几个问题:模型服务密钥、工具凭据和数据库账号要分开;模型不接触数据库账号;订单查询靠服务端上下文中的租户标识和客户标识兜底;连不上时按连接、认证、存储格式的顺序排查;出问题后通过请求 ID 关联服务端和数据库日志。它们不是一次性安全保证,而是需要随着实际权限模型和数据库行为持续验证。

参考资料

[1] OWASP Secrets Management Cheat Sheet
https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html

[2] MCP Security Best Practices
https://modelcontextprotocol.io/specification/draft/basic/security_best_practices

[3] PostgreSQL Row Security Policies
https://www.postgresql.org/docs/current/ddl-rowsecurity.html

返回列表