<p>视图安全需显式列名、回收基表权限、脱敏字段重命名、禁用元数据访问。SELECT * 视图会暴露新增列,权限不回收则形同虚设,别名须语义明确,系统表权限必须严格限制。</p>

CREATE VIEW 必须显式列出字段,禁用 SELECT *
视图不自动过滤列,它只暴露你写进 SELECT 子句的字段。用 SELECT * 就等于把基表所有列(包括后续新增的 salary_revision_date、internal_notes)全兜进来,哪怕应用只查 name 和 email,元数据里照样暴露 password_hash 字段名和类型。
MySQL 中基表加新列后,SELECT * 视图会自动包含它;显式列表则完全免疫。PostgreSQL 要求含表达式时必须声明列名,比如:CREATE VIEW v AS SELECT email AS user_email FROM users;
- 正确写法:
CREATE VIEW user_safe AS SELECT id, name, email, created_at FROM users; - 敏感字段名要核对真实拼写,比如
credit_card_number和cc_number可能是不同字段 - 原表后续新增敏感列(如审计字段),视图不会自动排除,需人工更新定义
必须 REVOKE 基表权限,否则视图形同虚设
用户能直查 users 表,SELECT * FROM users 一次就全暴露了。视图只是查询模板,真正起作用的是权限回收动作。
先执行:REVOKE SELECT ON users FROM app_user;(PostgreSQL/MySQL 通用)
再执行:GRANT SELECT ON user_safe TO app_user;
- PostgreSQL 额外要:
REVOKE USAGE ON SCHEMA public FROM app_user;,否则可能通过public.users显式引用绕过 - 验证方式:
SHOW GRANTS FOR 'app_user'@'%';,输出里不能出现ON mydb.users - MySQL 8.0+ 对
information_schema的REVOKE SELECT不生效,得靠skip-show-databases+ 仅授目标库权限
脱敏字段必须重命名,别留语义陷阱
写 CONCAT(LEFT(phone, 3), '****', RIGHT(phone, 4)) AS phone,列名还是 phone。前端代码可能直接拿这个值做短信校验,ORM 可能反向映射更新原字段,审计工具也识别不出这是掩码值。
必须用明确语义的别名:AS phone_masked 或 AS email_domain,传递信号。
- NULL 值必须预处理:
CONCAT(LEFT(IFNULL(TRIM(phone), ''), 3), '****', RIGHT(IFNULL(TRIM(phone), ''), 4)) AS phone_masked -
WHERE条件别依赖phone_masked——它是运行时计算值,无法走索引,会导致全表扫描 - SQL Server 中若基表列启用了 Always Encrypted,视图无法解密,但列本身仍存在;必须从
SELECT中剔除才能真正隔离
关掉 SHOW CREATE VIEW 和系统元数据访问
SHOW CREATE VIEW 会原样返回视图定义,包括字段别名、CASE WHEN 脱敏逻辑、甚至注释里的业务规则。攻击者拿到定义,就能推断出原始字段名和掩码规律。
MySQL:执行 REVOKE SHOW VIEW ON *.* FROM 'app'@'%';
PostgreSQL:确保用户没有 pg_read_all_data,且未被显式授予 pg_get_viewdef() 执行权限
SQL Server:撤销 VIEW DEFINITION 后,sp_helptext 和 sys.dm_exec_des 也不该返回定义
- INFORMATION_SCHEMA 和
pg_catalog是最大漏洞口——只要用户能查这些系统表,就能反推出视图用了哪些基表、哪些字段、甚至脱敏逻辑 - PostgreSQL 必须
REVOKE USAGE ON SCHEMA pg_catalog FROM app_user,再逐个撤回pg_tables、pg_views、pg_columns - 所有数据库都得禁用
PROCESS和FILE权限,防止错误日志间接泄露路径或上下文
真正的安全不是靠“看不见”,而是靠“查不到+读不了+推不出”。每个环节松动一点,整个视图防线就塌一半。

















