临时表权限不能靠 GRANT SELECT ON ALL TABLES 解决,因 PostgreSQL 临时对象在 pg_temp 命名空间,需显式授予 TEMP 权限;只读授权还需禁用 TRUNCATE、REFERENCES、SET 等隐式写操作,并防范工具侧越权与权限继承漏洞。
临时表权限为什么不能靠 GRANT SELECT ON ALL TABLES 解决
很多团队给分析师开库权限时,图省事直接执行
,结果发现:临时表(
或
生成的隐式临时结果)照样查不了,甚至
这种简单 cte 都报错 —— 因为 postgresql 的
权限不覆盖临时命名空间,而临时对象默认在
下创建,它压根不属于任何 schema,也不受 schema 级权限控制。
临时表的权限由会话级
或
权限决定,必须显式授予:
即使开了
,如果查询里用到
或物化 CTE(如
),仍需额外
权限(不推荐给分析师)
MySQL 和 SQL Server 没有独立的
权限概念,但临时表实际依赖
全局权限,且只对
语句生效,对子查询或派生表无影响
只读授权必须禁用哪些隐式写操作
“只读”不是加个
就完事。分析师用的 BI 工具(比如 Metabase、Superset)或 Python 的
默认会悄悄发写命令,一旦权限漏掉一个,就可能意外改库。
PostgreSQL 必须收回
、
(可被用于绕过行级策略)、
,否则
+
可能触发函数侧信道泄露
禁止
类命令:分析师若能执行
,可能干扰监控;更危险的是
,可切换到恶意 schema 执行同名函数
MySQL 要关掉
权限(防
写文件)、
(防看到其他会话 SQL)、
(防绕过只读模式)
如何让临时表和只读权限真正协同生效
只读角色 + 临时表权限 ≠ 安全。常见陷阱是:分析师用
建好临时表后,再执行
—— 这就绕过了只读限制,因为临时表本身不受只读约束。
解决方案不是堵死
,而是用行级安全策略(RLS)+ 策略函数强制拦截:
(注意:PostgreSQL 不支持对
schema 直接建 RLS,需改用 session-level check constraint 模拟)
更务实的做法:用视图封装常用中间逻辑,禁止直接
,改用
子句,并在连接池层(如 pgbouncer)或代理层(如 ProxySQL)拦截含
的语句
ClickHouse 用户注意:
在 22.8+ 后默认关闭,需显式开启
,且该权限无法按用户粒度控制,只能靠配置文件全局开关
权限回收时最容易被忽略的三个点
分析师离职或转岗后,删掉角色或撤回
很容易,但以下三点常被跳过,导致残留访问能力。
中的临时对象不会随会话结束自动清理干净:若分析师曾用
创建过同名表,后续新会话可能复用旧结构,而旧权限未清,造成“幽灵访问”
某些 BI 工具(如 Tableau)会缓存元数据并预建物化视图,这些视图可能以高权限账号运行,分析师通过查询视图间接获得越权结果
PostgreSQL 的
属性:如果
继承了
,而后者有
权限,那么即使你 revoke 了 analyst 的所有权限,只要没
,继承链仍有效
临时表权限和只读授权的边界,从来不在 SQL 语法层面,而在会话生命周期、工具行为和权限继承模型里——漏掉任意一层,都可能让“只读”变成纸面约定。
grant select on all tables in schema public to analyst_rolecreate temp tablewithselect * from (values (1),(2)) t(a)selectpg_tempTEMPTEMPORARYGRANT TEMP ON DATABASE your_db TO analyst_roleTEMPCREATE TABLE ASWITH data AS MATERIALIZED (...) SELECT ...CREATETEMPCREATE TEMPORARY TABLESCREATE TEMPORARY TABLEREVOKE INSERT, UPDATE, DELETEsqlalchemyTRUNCATEREFERENCESTRIGGERSELECTREFERENCESSETSET statement_timeout = '1s'SET search_pathFILESELECT ... INTO OUTFILEPROCESSSUPERCREATE TEMP TABLE t AS SELECT ...INSERT INTO t VALUES (...)INSERTCREATE POLICY temp_insert_block ON pg_temp.t FOR INSERT USING (false)pg_tempCREATE TEMP TABLEWITHCREATE TEMPCREATE TEMPORARY TABLEallow_experimental_temporary_tables = 1SELECTpg_tempCREATE TEMP TABLE IF NOT EXISTSINHERITanalyst_roledeveloper_roleCREATENOINHERIT