必须回收基表select权限并用with execute as owner创建视图,否则用户可绕过视图直查敏感数据;需禁用动态函数、显式列名、剔除敏感字段,并用目标账号双重验证。

只读业务数据集不能靠“建个视图 + GRANT SELECT”就完事,SQL Server 里视图本身不带权限边界,用户只要对底层表还有 SELECT 权限,就能绕过视图直接查基表——你精心定义的字段过滤和行过滤形同虚设。
必须先回收基表直连权限
很多人执行完 GRANT SELECT ON v_orders_summary TO report_user 就以为安全了,但只要 report_user 还保有对 orders 表的 SELECT 权限,它就能 SELECT * FROM orders 看到所有字段,包括 payment_card_last4、internal_notes 这类敏感列。
- 显式执行
REVOKE SELECT ON dbo.orders FROM report_user(逐表回收,别用REVOKE ALL) - 检查残留:运行
SELECT permission_name, object_name FROM sys.database_permissions p JOIN sys.objects o ON p.major_id = o.object_id WHERE grantee_principal_id = USER_ID('report_user') - 如果用户属于
db_datareader角色,必须先EXEC sp_droprolemember 'db_datareader', 'report_user',否则角色权限会覆盖你手动收回的表级权限
视图定义要禁用动态上下文函数
SQL Server 视图若引用 CURRENT_USER()、SUSER_SNAME() 或 SESSION_CONTEXT() 等函数,可能让不同用户查出不同行——这在第三方系统接入时极易引发越权或空结果,且排查困难。
- 避免在
WHERE中写WHERE created_by = CURRENT_USER(),这类逻辑应移到应用层或 API 层做鉴权 - 禁止使用
NEXT VALUE FOR序列,视图不可更新,调用序列无意义还可能报错 - 字段列表必须显式写出,严禁
SELECT *;敏感列如is_deleted、tenant_id、audit_log必须从SELECT子句中剔除
SQL SECURITY 不是选项,是必需声明
SQL Server 默认按 INVOKER 模式执行视图(即检查调用者权限),而你需要的是 DEFINER 模式——让视图以创建者身份运行,只依赖创建者对基表的权限,不穿透校验调用者。
- 创建时必须加
WITH EXECUTE AS OWNER,例如:CREATE VIEW v_customer_public WITH EXECUTE AS OWNER AS SELECT id, name, email FROM customers WHERE status = 'active' - 不加该子句时,即使你已回收
report_user对基表的权限,查询视图仍会报错permission denied on object 'customers' - OWNER 必须是拥有基表
SELECT权限的账号(通常是dbo或 DBA),不能是普通应用账号
别把物化视图当实时只读接口用
SQL Server 没有原生物化视图,但有人用索引视图(CREATE VIEW ... WITH SCHEMABINDING + 唯一聚集索引)模拟。这会带来两个隐蔽风险:
- 索引视图要求基表所有引用列必须为确定性表达式,
GETDATE()、NEWID()、ISNULL(col, '')等都会导致创建失败 - 一旦基表结构变更(如加列、改类型),索引视图会自动失效,但查询仍能执行——只是退化为普通视图,性能骤降且你可能完全不知情
- 如果你的目标是“实时只读”,就别碰索引视图;它解决的是高频聚合查询性能问题,不是权限隔离问题
最易被忽略的一点:视图权限生效后,必须用目标账号(如 report_user)在 SSMS 或 sqlcmd 中实际登录,执行 SELECT TOP 1 * FROM v_customer_public 和 SELECT TOP 1 * FROM customers 双重验证——前者成功、后者报错,才算真正闭环。










