首页 > 解决方案 > SQL Server:表的行级安全性未在另一个谓词函数中强制执行

问题描述

考虑以下情况:有两个表,表 A 和表 B。在表 A 上实施 RLS。

然后,在表 B 上通过内部连接将表 B 链接到表 A 来实现 RLS。猜猜看,在表 B 的谓词函数中,表 A 的 RLS 被忽略了。

  1. 看来,您需要在表 B 的谓词函数中重新应用表 A 的 RLS。这是设计使然吗?

  2. 在谓词函数中将表 B 链接到表 A 时,是否可以设置一些标志或“with”子句以强制执行现有的 RLS?

  3. 难道我做错了什么?

用于创建和填充表的 SQL 脚本。

CREATE TABLE [dbo].[Securities]
(
    [SecurityId] [int] NOT NULL,
    [Security] [varchar](50) NOT NULL,
    [IssueCurrency] [varchar](3) NOT NULL,

    CONSTRAINT [PK_Securities] 
        PRIMARY KEY CLUSTERED ([SecurityId] ASC)
                    WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, 
                          IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, 
                          ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO

CREATE TABLE [dbo].[Prices]
(
    [SecurityId] [int] NOT NULL,
    [PriceDate] [date] NOT NULL,
    [Price] [numeric](18, 4) NULL,

    CONSTRAINT [PK_Prices] 
        PRIMARY KEY CLUSTERED ([SecurityId] ASC, [PriceDate] ASC)
                    WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, 
                          IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, 
                          ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO

ALTER TABLE [dbo].[Prices] WITH CHECK 
    ADD CONSTRAINT [FK_Prices_Securities] 
        FOREIGN KEY([SecurityId]) REFERENCES [dbo].[Securities] ([SecurityId])
GO

ALTER TABLE [dbo].[Prices] CHECK CONSTRAINT [FK_Prices_Securities]
GO

INSERT INTO [dbo].[Securities] ([SecurityId], [Security], [IssueCurrency])
VALUES (1, 'Project Power', 'CAD'), (2, 'Fractured Ltd', 'AUD'),
       (5, 'Eddy Security', 'USD'), (6, 'Foxtrot', 'USD')
GO

INSERT INTO Prices ([SecurityId], [PriceDate], [Price])
VALUES (1, '2020-08-05', 13.2500), (1, '2020-08-06', 13.3500), (1, '2020-08-07', 13.0500),
       (2, '2020-08-05', 23.9500), (2, '2020-08-06', 24.1500), (2, '2020-08-07', 22.0500),
       (5, '2020-08-05', 105.1500), (5, '2020-08-06', 106.3500), (5, '2020-08-07', 105.0500),
       (6, '2020-08-05', 36.2500), (6, '2020-08-06', 36.3500), (6, '2020-08-07', 35.0500)

用于创建谓词函数和安全策略的 SQL 脚本。

/*
drop security policy if exists Policy_Securities
go 

drop function if exists dbo.rlsSecurities
go
*/

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

CREATE FUNCTION rlsSecurities (@IssueCurrency varchar(3))
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN 
    SELECT 1 AS UserAccess
        WHERE
            (SESSION_CONTEXT(N'UserGroup') = 'Internal')  or
            (
                @IssueCurrency = 'USD'      --non-internal group can only view securities where IssueCurrency='USD'         
            )
go

CREATE SECURITY POLICY Policy_Securities
ADD FILTER PREDICATE dbo.rlsSecurities(IssueCurrency) ON dbo.Securities
WITH(STATE = ON)
go

EXEC sys.sp_set_session_context @key=N'UserGroup', @value='XYZ'

SELECT SESSION_CONTEXT(N'UserGroup')

SELECT * FROM Securities

SELECT s.Security, p.*
FROM Securities s
INNER JOIN Prices p ON s.SecurityId = p.SecurityId  --RLS on table Prices is enforced thru inner join.

SELECT * FROM Prices  --Currently no RLS on tblComp.
--Now I want to implement RLS on Prices as well.
drop security policy if exists Policy_Prices
go 

drop function if exists dbo.rlsPrices
go

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
create FUNCTION rlsPrices (
    @SecurityId int)
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN 

    SELECT 1 as UserAccess
        where @SecurityId in (select SecurityId from dbo.Securities)  --This doesn't work even though RLS on table Securities already exists.
        
        --where   --This works but it requires reapplying RLS on table Securities.
        --  (SESSION_CONTEXT(N'UserGroup') = 'Internal')  or
        --  (
        --      @SecurityId In (select SecurityId from dbo.Securities where IssueCurrency = 'USD')      --non-internal group can only view securities where IssueCurrency='USD'         
        --  )
go

CREATE SECURITY POLICY Policy_Prices
ADD FILTER PREDICATE dbo.rlsPrices(SecurityId) ON dbo.Prices
WITH(STATE = ON)

go
exec sys.sp_set_session_context @key=N'UserGroup', @value='XYZ'
select SESSION_CONTEXT(N'UserGroup')
select * from Prices    --RLS not enforced

标签: sql-serverrow-level-security

解决方案


你没有做错任何事。

当 tvf 在策略的范围/执行中执行时,似乎 RLS 没有考虑谓词表值函数中引用的表上的任何现有策略。在正常查询中执行时,tvf 工作正常(强制执行 tvf 中引用表 {ie. Securities} 的策略):

--standalone...check the execution plan, predicate of Securities.PK_Securities index scan is the function/policy on currency='USD'
select p.* 
from Prices as p
cross apply dbo.rlsPrices(p.SecurityId)

如果您检查执行计划:

--filter policy on Prices
select * from Prices

您只会看到对价格的聚集索引扫描,因为从价格到证券 (SecurityId) 存在一个 FK,因此根本不检查证券表。即使您删除 FK,价格和证券之间的连接也会错过任何关于证券的 RLS 谓词。

简而言之,过滤器策略不是嵌套/继承/级联的,无论是否设计......尚不清楚。


推荐阅读