参数化(始终加密)- 内部存储过程

Posted

技术标签:

【中文标题】参数化(始终加密)- 内部存储过程【英文标题】:Parameterization(Always Encrypted)- Inside stored proc 【发布时间】:2018-11-01 13:55:29 【问题描述】:

我有一个场景,我需要在 proc 中使用文字(硬编码字符串)来对抗“始终加密”列,因为这会失败并出现以下错误,

操作数类型冲突:varchar 与 nvarchar(20) 不兼容 用 (encryption_type = 'DETERMINISTIC', 加密算法名称 = 'AEAD_AES_256_CBC_HMAC_SHA_256', column_encryption_key_name = 'CEK_Auto4', column_encryption_key_database_name = 'DBName')

我正在尝试在存储过程中为 Always Encrypted 进行参数化,类似于下面

GO
    CREATE PROCEDURE InsertProc  
    @Var1 nVarchar(20)  
    As  
    BEGIN     
        DECLARE @Plaintext nvarchar(20)='testText' 

        INSERT INTO testClass(EncryptedCol1,EncryptedCol2,NonEncryptedCol)
        VALUES (@Plaintext,@Var1,default)
    END

我还启用了参数化并启用了连接的列加密设置。创建过程还是报如下错误,

过程 sp_describe_parameter_encryption,第 1 行 [批处理开始行 4] 无法准备报表。时发生错误 执行批处理。错误信息是:内部错误。元数据 语句中的参数“@p3467a2cdc3d547a3be48f46dfc7e9580”或 procedure ' 在返回的结果集中丢失 sp_describe_parameter_encryption。

注意:如果我手动运行 proc 中的脚本,我仍然可以执行和插入。但是 Proc 创建是问题

有人可以帮助解决或提供替代解决方案吗?

【问题讨论】:

你不能这样做。它的构建方式是围绕加密列从未出现在服务器上的明文(直到 2019 年使用安全飞地,但这只是移动了一些东西,它仍然不会作为明文出现在飞地) 手动执行它的原因是因为 SSMS 不遗余力地将命令转换为正确使用加密的命令。不幸的是,这只能在客户端工作。不可能让服务器在加密列中插入明文值,因为如果是这样,那将意味着服务器知道加密密钥,这与 Always Encrypted 的整个想法背道而驰。 那么是否没有方案可以针对硬编码文字处理加密列?从应用程序发送所有文字作为参数是唯一的选择吗?我急需解决! 我知道您目前专注于您的特定用例,但请考虑一个允许某人在帐户之间转移资金的存储过程,其中帐号被加密。 Always Encrypted 旨在保护我们的数据免受数据库管理员等的侵害。您是否真的希望其中一个能够临时更改此存储过程的定义,以便它使用硬编码的文字来表示收件人的他们的帐号? 嗯,我明白了,我应该开始一段艰难的旅程,重新设计我的场景。 【参考方案1】:

始终加密的主要目的是使 SQL Server(因此您的 DBA)无法解密您的敏感数据。为了实现这一点,SQL Server 绝对无法访问加密密钥,也无法加密和解密数据。所有加密和解密均由客户端(您的应用程序、SSMS 等)执行。为 Always Encrypted 启用参数化是 SSMS 的一项功能。启用后(并且您为连接指定 Column Encryption Setting=Enabled),SSMS 将检测脚本中声明的变量,将其转换为参数化查询并执行转换后的版本。这样,参数值将在客户端加密,SQL Server 根本看不到它们的纯文本值。

在您的示例中,@Plaintext 不是脚本中声明的变量,而是存储过程的局部变量。因此,这段代码(值的分配)将在 SQL Server 的引擎中执行,并且由于它无法访问加密密钥,因此根本不可能对值进行加密。所以你也需要把它作为存储过程的输入参数。在这种情况下,很容易给它一个默认值并在调用过程时忽略它,但默认值是存储在元数据中的东西,它是明文(未加密)的。必须有人对其进行加密,而唯一能做到这一点的是客户端。因此,您需要从客户端传递它,而不是使其成为默认值,即无法在服务器中使用“硬编码值”。

如果这对您来说是一个主要障碍,则可能 Always Encrypted 技术不适合您的情况。如果您使用带有证书的列级加密,您将能够做所有这些事情。但这破坏了 Always Encrypted 的主要优势 - 您的 DBA 无法访问您的秘密。

【讨论】:

【参考方案2】:

我找到了在程序中使用硬编码字符串的解决方法。

    创建具有 nVarchar 列的表(注意:加密的 nvarchar 与加密的 varchar 不兼容,您也不能将较高数据长度的值分配给较低数据长度的加密值(例如,您不能分配加密的变量/列nVarchar(max) 转换为 nVarchar(20)) 的加密列。所以尽量选择更小的大小。) 使用与所考虑的列相同的加密类型和密钥对该列进行加密。 现在您可以使用表中的值进行插入、更新、比较等操作。操作。您还可以在诸如 coalesce 、 isnull 等函数中使用。

例子:

用加密列创建表

CREATE TABLE [dbo].[Encrypted_nVarchar_256](
    [SNo] [smallint] IDENTITY(-32768,1) NOT NULL,
    [EncryptValue] [nvarchar](256) COLLATE Latin1_General_BIN2 
ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [CEK_key], ENCRYPTION_TYPE = Deterministic, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256') NULL,
        [Value_Description] [nvarchar](256) NULL,
     CONSTRAINT [Pk_Enc_nVar_256] PRIMARY KEY CLUSTERED 
    (
        [SNo] 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

在加密列中插入您需要在过程中使用的值。

DECLARE @EncryptValue  nvarchar(256) ='';
INSERT INTO Encrypted_nVarchar_256(EncryptValue,[Value_Description]) VALUES(@EncryptValue,'Empty string')
DECLARE @EncryptValue1  nvarchar(256) ='Some string you would like to hard code';
INSERT INTO Encrypted_nVarchar_256(EncryptValue,[Value_Description]) VALUES(@EncryptValue1,'Your description')
--more rows as you need
GO

现在您可以在存储过程中使用

 CREATE PROCEDURE InsertProc  
    @Var1 nVarchar(20)  
    As  
    BEGIN    
        --Passing hard coded ''(Empty string) as insert value
        INSERT INTO testTable(EncryptedCol1,EncryptedCol2,NonEncryptedCol)
        SELECT TOP 1 EncryptValue,@Var1,"some string" FROM Encrypted_nVarchar_256 where Sno=-32768
        --Comparing some hard coded string
        SELECT * from  testTable
        where EncryptedCol1=(SELECT TOP 1 EncryptValue FROM Encrypted_nVarchar_256 
 where Sno=-32768)
         --Using in functions
         SELECT COALESCE(EncryptedCol1,(SELECT TOP 1 EncryptValue FROM Encrypted_nVarchar_256  where Sno=-32768)) as [new col name] from  testable

    END

瞧!!!现在你可以做任何事情,只要稍微扭动一下。

【讨论】:

以上是关于参数化(始终加密)- 内部存储过程的主要内容,如果未能解决你的问题,请参考以下文章

MySQL存储过程

MySQL 存储过程 - 如何引用内部创建的记录集

如何从内部调用获取记录集到存储过程?

嵌入式中利用内部flash存储参数

MySQL 存储过程传参之in, out, inout 参数用法

我们可以使用雪花中的存储过程将文件从文件位置加载到命名内部阶段吗?