加密存储过程正在加密计划缓存查询计划

Fru*_*aun 5 sql-server stored-procedures encryption

在查询与存储过程加密相关的计划缓存时,我遇到了一个非常奇怪的问题。但是,查询加密计划缓存的是存储过程,而不是计划缓存本身中的查询。让我解释...

下面的过程只是在计划缓存中搜索匹配的字符串并输出查询计划(最后一列)。

    ALTER PROCEDURE spQueryPlanCache @textstring nvarchar(max)
    AS
    BEGIN
        SELECT 
                databases.name,
            dm_exec_sql_text.text AS TSQL_Text,
            dm_exec_query_stats.creation_time, 
            dm_exec_query_stats.execution_count,
            dm_exec_query_stats.total_worker_time AS total_cpu_time,
            dm_exec_query_stats.total_elapsed_time, 
            dm_exec_query_stats.total_logical_reads, 
            dm_exec_query_stats.total_physical_reads, 
            dm_exec_query_plan.query_plan
        FROM sys.dm_exec_query_stats 
        CROSS APPLY sys.dm_exec_sql_text(dm_exec_query_stats.plan_handle)
        CROSS APPLY sys.dm_exec_query_plan(dm_exec_query_stats.plan_handle)
        INNER JOIN sys.databases
        ON dm_exec_sql_text.dbid = databases.database_id
        WHERE dm_exec_sql_text.text LIKE @textstring
        
    END
Run Code Online (Sandbox Code Playgroud)

这是最后一列中输出的计划 XML 的示例:

    ALTER PROCEDURE spQueryPlanCache @textstring nvarchar(max)
    AS
    BEGIN
        SELECT 
                databases.name,
            dm_exec_sql_text.text AS TSQL_Text,
            dm_exec_query_stats.creation_time, 
            dm_exec_query_stats.execution_count,
            dm_exec_query_stats.total_worker_time AS total_cpu_time,
            dm_exec_query_stats.total_elapsed_time, 
            dm_exec_query_stats.total_logical_reads, 
            dm_exec_query_stats.total_physical_reads, 
            dm_exec_query_plan.query_plan
        FROM sys.dm_exec_query_stats 
        CROSS APPLY sys.dm_exec_sql_text(dm_exec_query_stats.plan_handle)
        CROSS APPLY sys.dm_exec_query_plan(dm_exec_query_stats.plan_handle)
        INNER JOIN sys.databases
        ON dm_exec_sql_text.dbid = databases.database_id
        WHERE dm_exec_sql_text.text LIKE @textstring
        
    END
Run Code Online (Sandbox Code Playgroud)

一切看起来都很正常 - 我的计划缓存存储过程在计划缓存中找到了一个名为spMySproc的过程,我可以看到语句文本。伟大的!

但是......如果我将WITH ENCRYPTION添加到我的计划缓存查询过程(spQueryPlanCache)的定义中并重新运行它,则由我的加密存储过程输出的计划缓存中的query_plan列本身已被加密! !

 <StmtSimple StatementText="CREATE PROCEDURE [dbo].[spMySproc];1 (@...

Run Code Online (Sandbox Code Playgroud)

这对我来说毫无意义。我期望发生这种情况的唯一情况是 spMySproc 也被加密,但事实并非如此!如果我再次查询计划缓存,我可以看到 spMySproc 未加密。

这是一个已知的错误,还是有人可以解释为什么这对他们有意义?

这是一个显示该问题的最小、完整且可验证的示例:

CREATE OR ALTER PROCEDURE dbo.test_proc
AS
BEGIN
    SELECT 0xAEFB1FA7285840C3FF30935AE0775433E669A38EBD60091BE1990D68819F346D;
END
GO
CREATE OR ALTER PROCEDURE dbo.spQueryPlanCache @textstring nvarchar(max)
WITH ENCRYPTION
AS
BEGIN
    SELECT deqp.query_plan
    FROM sys.dm_exec_query_stats deqs
        CROSS APPLY sys.dm_exec_sql_text(deqs.plan_handle) dest
        CROSS APPLY sys.dm_exec_query_plan(deqs.plan_handle) deqp
    WHERE dest.text LIKE '%0xAEFB1FA7285840C3FF30935AE0775433E669A38EBD60091BE1990D68819F346D%'
        AND deqp.query_plan IS NOT NULL;
END
GO

--output from this will contain the query plan, with the StatementText column "encrypted".
EXEC dbo.spQueryPlanCache @textstring = '%0xAEFB1FA7285840C3FF30935AE0775433E669A38EBD60091BE1990D68819F346D%';

GO
CREATE OR ALTER PROCEDURE dbo.test_enc
(
    @xml nvarchar(max)
)
WITH ENCRYPTION
AS
BEGIN
    SELECT CONVERT(xml, @xml);
END
GO

DECLARE @val nvarchar(max);

SELECT TOP(1)
    @val = CONVERT(nvarchar(max), deqp.query_plan)
FROM sys.dm_exec_query_stats deqs
    CROSS APPLY sys.dm_exec_sql_text(deqs.plan_handle) dest
    CROSS APPLY sys.dm_exec_query_plan(deqs.plan_handle) deqp
WHERE dest.text LIKE '%0xAEFB1FA7285840C3FF30935AE0775433E669A38EBD60091BE1990D68819F346D%'
    AND deqp.query_plan IS NOT NULL;

--output here will show the same query plan, but the STatementText won't be encrypted.
EXEC dbo.test_enc @val;
Run Code Online (Sandbox Code Playgroud)

2014年、2016年和2019年也出现了同样的行为。

war*_*yen 1

您观察到的行为是预期的,不是错误。当您将WITH ENCRYPTION 选项添加到存储过程时,它会加密存储过程的定义,包括查询文本。因此,该加密存储过程的任何输出(例如您的情况下的 query_plan 列)也将被加密。

为了进一步澄清,加密应用于存储过程的定义,而不是计划缓存中的各个查询。计划缓存存储已编译的计划,但是当执行加密的存储过程时,它会解密定义并在执行过程中动态生成查询计划。但是,计划中的实际查询文本仍然是加密的。

在您的情况下,当您执行具有WITH ENCRYPTION选项的spQueryPlanCache存储过程时,返回的查询计划将具有加密的语句文本,因为存储过程本身是加密的。此行为是有意设计的,旨在保护加密存储过程的定义不被泄露。

如果需要在不加密的情况下访问查询计划,则需要从 spQueryPlanCache 存储过程中删除WITH ENCRYPTION 选项,或者使用单独的非加密存储过程来检索查询计划。

这是未加密的脚本的更新版本:

CREATE OR ALTER PROCEDURE dbo.spQueryPlanCache @textstring nvarchar(max)
AS
BEGIN
    SELECT deqp.query_plan
    FROM sys.dm_exec_query_stats deqs
        CROSS APPLY sys.dm_exec_sql_text(deqs.plan_handle) dest
        CROSS APPLY sys.dm_exec_query_plan(deqs.plan_handle) deqp
    WHERE dest.text LIKE '%' + @textstring + '%'
        AND deqp.query_plan IS NOT NULL;
END
Run Code Online (Sandbox Code Playgroud)