从应用真正需要的操作列权限
数据库连接成功只说明认证通过,不代表账号权限合适。应用处理普通请求时,通常需要读取和修改业务数据,不需要创建或删除表,更不需要管理其他角色。迁移过程则可能要改变结构和建立对象。将两种职责放在同一个长期连接账号上,会让应用获得远超日常请求所需的能力,错误操作的影响范围也随之扩大。
分开账号之前,先列出业务读写、文件登记、后台审计和定时发布涉及的表与操作。后台应用仍属于运行阶段,不能因为它有管理员界面,就直接使用数据库管理账号。部署脚本使用迁移账号的时间与入口也要明确,完成结构更新后不应把该凭据继续传给常驻应用。权限设计需要覆盖定时任务,而不只是网页请求。
区分登录角色与对象所有者
PostgreSQL 的角色既可以承载登录能力,也可以用于归属和授权组织。表所有者拥有与普通授权不同的控制能力,即使撤销部分常规权限,也不能把所有者简单等同于受限运行账号。一个可行模型是让独立所有者角色管理应用对象,迁移入口按规则获得相应能力,而应用登录账号只取得必要的数据访问许可。
如果项目规模较小,也可以先采用独立迁移登录账号拥有对象、应用账号获得授权的方式,逐步完善所有者与登录身份的分离。关键是先确认现有对象归谁所有,避免只新建账号却继续让应用拥有所有表。转移所有权会影响其他维护流程,应在隔离环境演练,并记录迁移工具需要执行的具体结构操作。
数据库和模式权限都要检查
连接数据库的权限与使用某个模式内对象的权限是不同层次。账号能够建立连接,但缺少模式使用权限时,可能仍无法访问表。相反,授予模式使用权限不等于授予表数据读写权限。应按数据库、模式、表和序列逐层核对,并避免为了快速解决一个拒绝访问错误,就把所有权限授给所有账号。
模式创建权限也需要单独考虑。运行账号如果能够在应用搜索路径中创建对象,可能影响对象解析及其他安全边界。检查应用采用的模式与搜索路径,并尽量使用明确的对象归属。不同 PostgreSQL 版本、初始化方式及历史授权会形成不同默认状态,因此不能仅根据默认安装印象推断当前数据库已经符合预期。
现有表和未来表需要两套处理
给现有表批量授权不会自动覆盖后来由迁移新建的表。默认权限可以为将来创建的对象设置授权,但它与对象创建角色和模式有关。应明确迁移时真正执行创建的身份,再针对该身份设置默认权限。若迁移工具切换到另一个角色创建对象,原来设置的默认授权可能并不会应用,这是新表上线后突然访问失败的常见来源。
默认权限也不会补齐历史对象,因此需要同时检查已有表授权和未来对象规则。对将来的序列、函数等对象,按实际需求分别处理。验证时创建一张临时业务样例表,使用运行账号执行允许的操作,再尝试结构修改确认被拒绝。测试结束由维护账号删除样例对象,并保留测试步骤,使下一次权限调整能够重复检查。
序列权限容易在插入时暴露
某些表的自增字段依赖序列,表插入权限正确但序列权限缺失时,仍可能在生成新编号的阶段报错。需要根据字段生成机制与实际语句核对序列操作需求。读取旧数据正常并不能覆盖这类错误,验收必须包含新增记录。也不要因为一个序列报错,就把所有结构对象的管理权限交给应用账号。
授权时只给运行阶段需要的序列使用能力,是否需要额外读取序列状态应由真实功能决定。不同自增实现和工具生成的结构可能有差异,先查看数据库对象定义,再编写授权规则。针对所有现有序列的批量操作适合专用应用模式;如果模式混合了其他系统对象,则应使用经过确认的对象名单,避免扩大授权范围。
函数、扩展和策略需要额外核对
项目如果通过函数访问数据,除了表权限还要检查函数执行权限、函数所有者和执行身份。具有提升权限语义的函数尤其需要审查搜索路径和输入处理。扩展安装或某些维护操作也可能要求更高权限,适合放到受控维护步骤。不能因为迁移中有一个特殊操作,就让日常应用一直以高权限连接数据库。
行级策略等机制还会继续限制表操作,传统授权通过并不证明每行都能够访问。权限拒绝和策略过滤可能表现为不同结果,例如报错或返回空集合。排查应检查具体账号、事务上下文与策略条件,避免为了修复一个查询为空的问题撤掉全部策略。本篇示例仅说明普通表读写,复杂策略必须结合业务模型另行验证。
对于连接池,还应注意会话级角色切换是否在请求结束后恢复。把维护操作与普通请求放在同一个池中,即使连接字符串采用运行账号,也可能因会话状态残留产生意外能力。优先使用独立连接入口,并在测试中复用连接检查身份是否稳定。账号边界应在长时间运行和多次请求后仍成立,不能只在首次连接时验证一次。
配置来源应与账号职责一致
常驻服务的连接变量应指向运行账号,结构更新工具使用独立配置来源。不要把迁移连接写入应用的备用配置,再指望所有开发者记得不用它。部署脚本需要在开始前检查当前账号与预期角色,避免配置文件路径错误导致维护命令意外使用运行账号,或应用意外使用维护账号。检查输出应只显示角色名称,不打印连接密码。
迁移配置文件应限制读取范围,并从部署包中排除。轮换凭据时先验证新连接,再替换对应配置并重启需要更新连接池的进程。已有数据库会话可能继续存在,必须按操作策略确认生效范围。迁移结束后检查应用实际连接角色,可以从受控诊断查询中观察,而不应把角色详情暴露到公开健康接口。
在隔离库验证允许与拒绝两侧
权限验收不能只看应用正常打开首页。使用运行账号验证读取、插入、更新和删除需要的对象,同时确认创建表、修改结构及管理角色等操作无法完成。测试数据应位于独立数据库或明确的专用样例中,不能在生产业务表上随意创建验证记录。事务回滚可以降低数据影响,但不能替代选择安全测试环境。
随后使用迁移账号执行一次代表性结构变更,并确认新对象对运行账号的授权正确。验收包含前后两侧,才能避免账号拆分后旧功能正常、新迁移对象却不可访问的问题。每次新增模式、序列或特殊函数时,更新授权脚本和测试场景。不要以所有命令都未报错作为唯一结论,还要核对实际角色及对象归属。
降权应保留可恢复的变更顺序
已有系统拆分账号时,先创建运行身份并补齐必要授权,在隔离环境及受控部署窗口验证,再切换应用连接。确认常驻服务、后台任务和备份维护各自采用正确身份后,才收紧原有广泛权限。不要先撤销正在运行的账号能力,再发现某个定时任务依赖它。整个过程应保留旧配置的安全恢复方式,并记录更改对象。
恢复旧连接配置并不一定撤销已经执行的结构变更。权限回退、应用回退和数据库结构兼容性需要分别考虑。对于只导入文章内容的发布,应使用已有运行权限完成数据写入,避免顺带执行结构初始化。这样既减少不必要的维护操作,也让内容部署的审计能够准确说明哪些数据发生了变化。
用权限清单作为长期维护入口
最终清单应写明对象创建者、运行角色、迁移角色、模式与默认授权。将授权脚本纳入版本管理,并在新建隔离库时执行,检查能否重现当前权限模型。临时手工授权需要及时回写到脚本,否则下一次恢复或迁移后会再次丢失。审计中只记录授权变化和执行身份,不保存明文认证信息。
下面示例假定两个角色和专用模式已经存在,维护角色负责新建对象。语句同时处理已有表、已有序列以及该创建角色的未来对象。运行账号不获得模式创建权限;具体数据操作仍应按项目需要缩减。执行前确认目标库与对象归属,执行后使用两种身份分别测试,不能把示例当作适用于所有共享数据库的通用授权脚本。
实现片段
-- 在隔离数据库验证;角色与 app 模式已由维护流程创建。
GRANT CONNECT ON DATABASE example_blog TO blog_app;
GRANT USAGE ON SCHEMA app TO blog_app;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA app TO blog_app;
GRANT USAGE ON ALL SEQUENCES IN SCHEMA app TO blog_app;
ALTER DEFAULT PRIVILEGES FOR ROLE blog_migrate IN SCHEMA app
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO blog_app;
ALTER DEFAULT PRIVILEGES FOR ROLE blog_migrate IN SCHEMA app
GRANT USAGE ON SEQUENCES TO blog_app;
-- 用运行账号检查当前身份和目标模式的能力。
SELECT current_user, current_database();
SELECT has_schema_privilege(current_user, 'app', 'USAGE') AS can_use,
has_schema_privilege(current_user, 'app', 'CREATE') AS can_create;
评论 · 0