必须先回收基表select权限再授予视图权限,视图须显式声明字段、过滤软删除行、设sql security definer并限制元数据访问,否则隔离失效。

先撤基表权限,再授视图权限
只建视图不回收原表权限,等于没设防。开发人员仍能直接执行 SELECT * FROM users 拿到所有字段,包括 password_hash、phone 这类敏感列。
必须分两步操作:
-
REVOKE SELECT ON mydb.users FROM 'dev'@'%';(MySQL)或REVOKE SELECT ON users FROM dev;(PostgreSQL) -
GRANT SELECT ON mydb.v_user_safe TO 'dev'@'%';(MySQL)或GRANT SELECT ON v_user_safe TO dev;(PostgreSQL)
执行后用 SHOW GRANTS FOR 'dev'@'%' 验证,输出里不能出现任何基表名(如 mydb.users)。
视图定义必须显式列字段,禁用 SELECT *
SELECT * 是元数据泄露的入口。基表一旦新增 internal_salary 列,视图立刻暴露它,且开发人员查 SELECT * FROM v_user_safe 就能拿到。
正确写法是硬编码字段列表:
CREATE VIEW v_user_safe AS SELECT id, username, email, created_at, status_masked FROM users WHERE deleted_at IS NULL;
注意:
- 别写
CONCAT(LEFT(phone, 3), '****') AS phone—— 列名还是phone,容易误导前端或 ORM;应写成CONCAT(LEFT(phone, 3), '****') AS p - 加
WHERE条件过滤软删除行,避免视图返回已逻辑删除的数据
SQL SECURITY 必须设为 DEFINER,且由高权账号创建
MySQL 默认是 SQL SECURITY DEFINER,但某些 ORM 或导出导入工具可能覆盖为 INVOKER。一旦是 INVOKER,开发人员执行视图时会按自己权限查基表——而你刚收回了他们的基表权限,结果就是查不到任何数据。
安全做法是显式声明并指定定义者:
CREATE DEFINER = 'admin'@'localhost' SQL SECURITY DEFINER VIEW v_user_safe AS ...
关键点:
-
DEFINER账号(如'admin'@'localhost')必须有基表的SELECT权限 - 开发账号
'dev'@'%'不需要、也不应该有该权限 - PostgreSQL 需额外执行
ALTER VIEW v_user_safe SET (security_invoker = false);
掐断元数据访问,防止逆向推导
开发人员只要能查 INFORMATION_SCHEMA.VIEWS 或 pg_views,就能看到视图定义里用了哪些字段、是否做了脱敏、甚至反推出原始列名(比如 status_masked 很可能对应 status)。
必须限制系统表访问:
- MySQL:执行
REVOKE SELECT ON INFORMATION_SCHEMA.VIEWS FROM 'dev'@'%'; - PostgreSQL:执行
REVOKE SELECT ON pg_views FROM dev;,并确认dev不在pg_read_all_data角色中 - 禁用
SHOW CREATE VIEW权限(MySQL),否则一行命令就暴露全部定义
真正难防的不是技术漏洞,而是权限链没断干净——哪怕漏掉一个 REVOKE,或者忘了检查视图定义里有没有 SELECT *,整个隔离就形同虚设。











