Sql server 行级安全删除块谓词和级联删除约束问题
考虑使用以下表格: 包含列的客户端表:Sql server 行级安全删除块谓词和级联删除约束问题,sql-server,row-level-security,Sql Server,Row Level Security,考虑使用以下表格: 包含列的客户端表: 客户端guid(唯一标识符-主键) 客户描述 带列的票证表: 票证guid(唯一标识符-主键) 票证\客户端\ guid(uniqueidentifier-客户端表的外键) 车票描述 带列的附件表: 附件\u guid(唯一标识符-主键) 附件\u票证\u guid(uniqueidentifier-票证表的外键,带有ON DELETE级联约束) 附件说明 考虑使用以下删除谓词函数,该函数应允许用户仅删除附件与特定描述匹配的票证: CR
- 客户端guid(唯一标识符-主键)
- 客户描述
- 票证guid(唯一标识符-主键)
- 票证\客户端\ guid(uniqueidentifier-客户端表的外键)
- 车票描述
- 附件\u guid(唯一标识符-主键)
- 附件\u票证\u guid(uniqueidentifier-票证表的外键,带有ON DELETE级联约束)
- 附件说明
CREATE FUNCTION [Security].[fn_ticket_delete](@ticket_guid AS SYSNAME)
returns TABLE
WITH schemabinding
AS
RETURN
SELECT 1 AS fn_securitypredicate_result
WHERE (SELECT attachment_description
FROM dbo.attachment
WHERE attachment_ticket_guid = @ticket_guid) = 'some specific attachment description'
然后我们有应用谓词fn的安全策略:
CREATE SECURITY POLICY [Security].[ticket_security]
ADD BLOCK PREDICATE [Security].[fn_ticket_delete]([ticket_guid]) ON [dbo].[ticket] BEFORE DELETE
WITH (STATE = ON, SCHEMABINDING = ON)
问题:无法删除票据,即使其附件描述与安全谓词条件匹配,因为附件记录信息不可用,行级安全检查时,DML已按照文档执行,并且已应用删除级联约束,记录已被删除,但尚未提交
注意:在应用相同条件的更新RLS限制的情况下,安全性工作得非常好,正如预期的那样
问题:如何克服此问题并使删除策略按预期工作?对于不存在的票证,估计的删除计划或实际的删除计划:
delete from ticket where ticket_guid = newid()
--or
delete from ticket where ticket_guid is null
显示安全功能“驻留”在两个“删除”之间:
最左边是T-SQL delete(delete语句)
右侧是票据的集群删除(&附件的级联删除)
在[嵌套循环的内部]行被删除/删除[嵌套循环的外部]之后,但在行的删除被提交[t-sql delete]之前,安全功能被“执行并评估”
高级描述可以是:对于每个票证行,删除票证和相应的附件,输出删除的外键:票证guid(序列运算符,由底部表池提供),对于每个删除的guid(嵌套循环,外部部分),执行安全功能(嵌套循环的内部部分)左半联接,因为安全函数无法返回任何结果,请评估/断言函数的结果是否为null或不为null,如果不为null,则票据行被“删除”并移动到下一票据
如果安全函数引用了其行被删除的任何表,那么这些表将在行删除后重新访问,如果函数的结果取决于已删除的行,那么安全性将始终失败
可以使用一个函数来测试这一点,该函数检查假定的“要删除”(实际上在之前已删除)票证guid是否存在:
create table dbo.ticketX
(
ticket_guid uniqueidentifier default(newsequentialid()) primary key clustered,
ticket_client_guid uniqueidentifier,
ticket_description varchar(10)
);
go
insert into dbo.ticketX(ticket_client_guid, ticket_description)
select v.guid, v.descr
from
(values (newid(), 'ticketA'), (newid(), 'ticketB'), (newid(), 'ticketC')) as v(guid, descr)
go
insert into dbo.ticketX(ticket_client_guid, ticket_description)
select v.guid, v.descr
from
(values (newid(), 'abc'), (newid(), 'ticketE'), (newid(), 'ticketF')) as v(guid, descr)
go
create function dbo.fn_ticketX_delete(@ticket_guid uniqueidentifier)
returns table
with schemabinding
as
return
(
select 1 as fn_securitypredicate_result
where exists(select 1 from dbo.ticketX where ticket_guid = @ticket_guid)
)
go
create security policy dbo.ticketX_security
add block predicate dbo.fn_ticketX_delete(ticket_guid) on dbo.ticketX before delete
with (state=on, schemabinding=on);
go
由于函数是在行删除后计算的,因此删除的票证guid不存在,函数不返回任何内容,并且安全检查始终失败:
--The attempted operation failed because the target object 'xyz.dbo.ticketX' has a block predicate that conflicts with this operation.
delete from dbo.ticketX;
某些行被删除(直到违反安全性并回滚整个语句)这一事实可以通过调整后的安全函数进行测试:
drop security policy dbo.ticketX_security;
go
--allow deletion of tickets with description like ticket%
create or alter function dbo.fn_ticketX_delete(@ticket_description varchar(10))
returns table
with schemabinding
as
return
(
select 1 as fn_securitypredicate_result
where @ticket_description like 'ticket%'
--where exists(select 1 from dbo.ticketX where ticket_guid = @ticket_guid)
)
go
create security policy dbo.ticketX_security
add block predicate dbo.fn_ticketX_delete(ticket_description) on dbo.ticketX before delete
with (state=on, schemabinding=on);
go
delete from dbo.ticketX
output deleted.* --some rows "get deleted"
问题:我怎样才能通过肮脏的阅读和制作来克服这个问题
删除策略是否按预期工作
没有肮脏的阅读。事实上,如果可以进行脏读,则安全功能可能会起作用:
RETURN
SELECT 1 AS fn_securitypredicate_result
WHERE (SELECT attachment_description
FROM dbo.attachment with(nolock) --if this were possible/enforced, it could work ??
WHERE attachment_ticket_guid = @ticket_guid) = 'some
由于create security policy语句有点误导,因此预期被颠覆。更准确的语句是:
创建安全策略xyz。。。添加块谓词。。。删除提交前删除行后代码>
一般来说,最好以确定性的方式接近RLS,即:在不受DML操作影响的常量和值上评估安全函数。在某种程度上,安全性是由其性质决定的。如果安全函数依赖于其他表(或不固定/持久的列/值*),则它将变得不确定(且不可预测),但最终结果,即安全策略评估,必须是确定的
*您可以尝试在tickets表上创建一个计算列,该列检查附件_description='some specific description'是否存在,并在安全函数中使用该列,但该列仍然存在(由于它访问数据,因此未持久化)是在删除票据和附件行后计算的,对于按照您设想的方式强制执行策略没有任何用处
让我们考虑只有在有一张票的情况下才能删除票。
附件链接到附件作者在
描述:'请删除我的票据{签名}'
如果您考虑这个需求,它与安全性无关==谁可以删除数据,它与根据您的业务规则的数据完整性有关:何时/在什么条件下可以删除数据。如果要将此逻辑封装在dbschema中,则附件上的触发器是执行此逻辑的最佳位置。这是对票证表上谓词的补充,以确保票证客户端guid与用户guid匹配,从而验证票证的删除访问权?此谓词用于验证在不删除票据的情况下删除附件的权限?这只是一个示例。如果票证链接的附件描述与特定字符串不匹配,则此谓词不允许删除票证。不要介意根据会话上下文检查用户guid,这只是为了简单起见。此外,这个问题与我之前发布的关于根据用户guid匹配票证\u客户端\u guid的问题完全不同。请考虑