是的,PostgreSQL 15 之前版本中 PUBLIC 角色默认对所有内置函数拥有 EXECUTE 权限,存在信息泄露与权限提升风险;15+ 版本已默认撤回,但存量库需手动回收高危函数权限并配置 ALTER DEFAULT PRIVILEGES 防止新增风险。
PostgreSQL 中 PUBLIC 角色默认有 EXECUTE 权限?
是的,postgresql 在 15 之前版本中,public 角色对所有内置函数(如 current_database()、version())默认拥有 execute 权限。这意味着任意用户(包括未显式授权的只读账号)都能调用这些函数——有些会泄露系统信息,有些可被用于权限提升探测。
- 典型风险场景:应用使用低权限数据库用户连接,但该用户仍能执行
pg_read_file()(若已启用 superuser 相关扩展)、pg_ls_dir()或带副作用的自定义函数 - 不是所有函数都危险,但「默认全开」违背最小权限原则;PostgreSQL 15+ 已修复此设计,默认撤回
PUBLIC的EXECUTE,但升级前存量库必须手动清理 - 检查方式:
SELECT proname, prokind, pg_get_userbyid(proowner) AS owner FROM pg_proc WHERE prosecdef AND NOT EXISTS (SELECT 1 FROM pg_namespace n JOIN pg_proc p ON p.pronamespace = n.oid WHERE n.nspname = 'pg_catalog' AND p.oid = pg_proc.oid);
更实用的是直接查权限:SELECT routine_name, privilege_type FROM information_schema.role_routine_grants WHERE grantee = 'PUBLIC' AND privilege_type = 'EXECUTE';
回收 PUBLIC 对 pg_catalog 函数的 EXECUTE 权限
不能直接 REVOKE EXECUTE ON ALL FUNCTIONS IN SCHEMA pg_catalog FROM PUBLIC——这会失败,因为部分函数(如 now())被核心机制依赖,撤销后可能导致 psql 启动报错或系统视图失效。
- 安全做法是按需逐个回收:优先处理已知高风险函数,例如
pg_read_file(text, int, int)、pg_ls_logdir()、pg_reload_conf()、pg_terminate_backend(int) - 执行命令示例:
REVOKE EXECUTE ON FUNCTION pg_read_file(text, int, int) FROM PUBLIC;
REVOKE EXECUTE ON FUNCTION pg_ls_dir(text) FROM PUBLIC;
- 注意函数签名必须完全匹配:同名函数可能有多个重载,
pg_read_file(text)和pg_read_file(text, int, int)是两个独立对象,需分别回收 - 回收后,超级用户仍可执行;普通用户若确实需要,应通过显式
GRANT到具体角色,而非开放给PUBLIC
如何避免误伤关键函数?
盲目批量回收容易让监控脚本、ORM 初始化逻辑或 DBA 工具失灵。关键是区分「业务无关的底层函数」和「常被客户端隐式调用的函数」。
- 安全黑名单函数(建议优先回收):
pg_read_file、pg_ls_dir、pg_logdir_ls、pg_stat_file、pg_sleep(防时间盲注)、dblink_connect - 谨慎处理函数(通常保留,除非确认无任何组件调用):
current_database()、version()、pg_typeof()、to_char()等类型转换/格式化函数——很多 ORM 和连接池会在启动时自动探测能力 - 验证方式:在非生产环境用目标应用账号连接,执行
\set VERBOSITY verbose后运行典型查询,观察是否触发permission denied for function xxx错误 - 回收操作本身不锁表,但建议在维护窗口执行,避免与正在部署的应用配置冲突
回收后还要检查哪些地方?
PUBLIC 的权限残留不止在函数上;如果数据库启用了扩展(如 adminpack、file_fdw),它们安装的函数也会被赋予 PUBLIC EXECUTE,且不会出现在 pg_catalog 中。
- 查扩展函数:
SELECT extname, p.proname FROM pg_extension e JOIN pg_depend d ON d.refobjid = e.oid JOIN pg_proc p ON p.oid = d.objid WHERE d.deptype = 'e';
- 常见高危扩展函数:
pg_file_read(file_fdw)、pg_logdir_ls(adminpack),需单独REVOKE - 别忘了自定义函数:开发人员创建的函数默认也会继承
PUBLIC EXECUTE,建函数时应加SECURITY DEFINER或显式REVOKE - 回收不是一劳永逸:新创建的函数默认仍会授予
PUBLIC,建议将ALTER DEFAULT PRIVILEGES加入部署流程:ALTER DEFAULT PRIVILEGES IN SCHEMA public REVOKE EXECUTE ON FUNCTIONS FROM PUBLIC;
最易被忽略的是扩展带来的函数权限——它们不在主 catalog 里,也不随 PostgreSQL 版本更新自动收紧,得人工盯住。










