SQL 服务器:table 上的行级安全性未在另一个谓词函数中强制执行
SQL Server : row-level security on table not enforced within another predicate function
考虑以下情况:有两个 table,table A 和 table B。在 table A 上实施 RLS。
然后,在 table B 上通过内部连接将 table B 链接到 table A 来实施 RLS。
猜猜看,在 table B 的谓词函数中,table A 的 RLS 被忽略了。
看来,您需要在 table B 的谓词函数中为 table A 重新应用 RLS。这是设计使然吗?
在谓词函数中将 table B 链接到 table A 时,是否可以设置一些标志或“with”子句以强制执行现有 RLS?
我是不是做错了什么?
SQL 用于创建和填充 table 的脚本。
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
你没有做错什么。
当 tvf 在 [=24= 中执行时,RLS 似乎没有考虑谓词 table-valued 函数 中引用的任何现有策略] 的政策。
tvf 在正常查询中执行时工作正常(执行 tvf 中引用的 table {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,因此根本不会检查证券 table。
即使您删除了 FK,价格与证券之间的联接也会遗漏证券上的任何 RLS 谓词。
简而言之,过滤策略不是nested/inherited/cascaded,是否有意设计..不清楚。
考虑以下情况:有两个 table,table A 和 table B。在 table A 上实施 RLS。
然后,在 table B 上通过内部连接将 table B 链接到 table A 来实施 RLS。 猜猜看,在 table B 的谓词函数中,table A 的 RLS 被忽略了。
看来,您需要在 table B 的谓词函数中为 table A 重新应用 RLS。这是设计使然吗?
在谓词函数中将 table B 链接到 table A 时,是否可以设置一些标志或“with”子句以强制执行现有 RLS?
我是不是做错了什么?
SQL 用于创建和填充 table 的脚本。
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
你没有做错什么。
当 tvf 在 [=24= 中执行时,RLS 似乎没有考虑谓词 table-valued 函数 中引用的任何现有策略] 的政策。 tvf 在正常查询中执行时工作正常(执行 tvf 中引用的 table {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,因此根本不会检查证券 table。 即使您删除了 FK,价格与证券之间的联接也会遗漏证券上的任何 RLS 谓词。
简而言之,过滤策略不是nested/inherited/cascaded,是否有意设计..不清楚。