部分包含场景下FileTable插入权限异常问题排查
问题描述
现有两个SQL Server数据库:
dbGateway:部分包含(partial containment)数据库dbGlobal:非包含数据库
环境配置:
- 跨库所有权链
cross db ownership chaining = 1 db chaining和trustworthy均处于关闭状态
操作步骤
- 在
dbGlobal中创建FileTable:
USE dbGlobal; CREATE TABLE tblData AS FileTable;
- 在
dbGateway中创建视图,用于访问dbGlobal的FileTable,并给角色SomeRole授权:
USE dbGateway; GO CREATE VIEW vwDataTable AS SELECT * FROM dbGlobal.dbo.tblData; GO GRANT SELECT ON vwDataTable TO SomeRole; GRANT INSERT ON vwDataTable TO SomeRole; GRANT DELETE ON vwDataTable TO SomeRole; GRANT UPDATE ON vwDataTable TO SomeRole; GO -- 当前用户执行插入正常 INSERT INTO vwDataTable (name, file_stream) VALUES ('Testing 12', 0x1),('Testing 13', 0x0);
- 使用
SomeRole下的用户SomeUserInRole执行操作时,SELECT、DELETE、UPDATE均正常,但INSERT失败:
GO EXECUTE AS USER = 'SomeUserInRole'; SELECT * FROM vwDataTable; UPDATE t SET file_stream = 0x2 FROM vwDataTable t WHERE name = 'Testing 12'; DELETE t FROM vwDataTable t WHERE name = 'Testing 13'; INSERT INTO vwDataTable (name, file_stream) SELECT 'This fails', 0x123; GO SELECT * FROM vwDataTable;
报错信息
State: S0005, error code: 229, Line: 18, ProcName: The SELECT permission was denied on the object 'tblData', database 'dbGlobal', schema 'dbo'.
即使将视图改为仅查询name和file_stream字段,问题依然存在。目前仅当给public授予tblData的SELECT权限时问题解决,但这种配置不安全:
USE dbGlobal; GRANT SELECT ON tblData TO public;
问题分析与解决方案
这是SQL Server的权限特性,而非Bug。
原因说明
FileTable的插入操作存在特殊权限检查逻辑:当向FileTable插入数据时,SQL Server会隐式执行对源表的SELECT操作(用于生成或验证FileTable的系统字段,比如path_locator、file_stream关联元数据等)。在部分包含数据库与非包含数据库的跨库交互场景中,跨库所有权链的权限传递规则无法覆盖这种隐式SELECT的权限需求,导致即使视图已授权,插入时仍会直接检查底层表的SELECT权限。
合理权限配置方案
方案1:给目标角色授予有限的SELECT权限
直接在dbGlobal中给SomeRole授予tblData的SELECT权限,而非开放给所有用户:
USE dbGlobal; GRANT SELECT ON dbo.tblData TO SomeRole;
该方案简单直接,既能满足插入时的隐式权限检查,又不会过度开放权限。
方案2:用存储过程封装插入操作(更安全)
通过存储过程封装插入逻辑,利用所有权链特性实现权限隔离:
- 在
dbGateway中创建由拥有dbGlobal.dbo.tblData完整权限的用户(如dbo)创建的存储过程:
USE dbGateway; GO CREATE PROCEDURE sp_InsertFileData @Name NVARCHAR(255), @FileStream VARBINARY(MAX) AS BEGIN SET NOCOUNT ON; INSERT INTO dbGlobal.dbo.tblData (name, file_stream) VALUES (@Name, @FileStream); END GO
- 给
SomeRole授予存储过程的执行权限:
GRANT EXECUTE ON sp_InsertFileData TO SomeRole;
这种方式下,SomeRole无需拥有dbGlobal中表的任何直接权限,通过存储过程的所有权链完成权限传递,安全性更高。
内容的提问来源于stack exchange,提问作者siggemannen
相关产品推荐
相关产品推荐

