web-dev-qa-db-ja.com

常に暗号化:暗号化されていない列と確定的暗号化列を等価結合するにはどうすればよいですか?

SQL Server 2017 での常に暗号化に関する公式のMicrosoftドキュメントは次のように述べています:

確定的暗号化では、特定のプレーンテキスト値に対して常に同じ暗号化値が生成されます。

確定的暗号化を使用すると、ポイント検索、等価結合、暗号化された列のグループ化とインデックス付けが可能になります。

(太字強調鉱山)

現在、SQL Server 2017 RTM-CU17(KB4515579)v14.0.3238.1 Standard Editionを使用しています。

私のSSMS(現在v18.4を使用)接続は、Enable Always Encrypted (column encryption)チェックボックスがオンになってすでに構成されており、クエリオプション->実行->詳細設定_Enable Parameterization for Always Encrypted_もオンになっています。

以下は私が持っているテーブルスキーマです。

EmployeeIDおよびFullName列は_Deterministic Encryption Type_で暗号化されます。

Temp列は_Randomized Encryption Type_で暗号化されています。

_SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[EmployeeTemperature]
(
    [Entry] [int] IDENTITY(1,1) NOT NULL,
    [CheckerID] [varchar](26) NOT NULL,
    [EmployeeID] [char](10) COLLATE Latin1_General_BIN2 ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [CEK_Auto1], ENCRYPTION_TYPE = Deterministic, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256') NOT NULL,
    [FullName] [varchar](50) COLLATE Latin1_General_BIN2 ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [CEK_Auto1], ENCRYPTION_TYPE = Deterministic, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256') NULL,
    [Temp] [decimal](4, 1) ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [CEK_Auto1], ENCRYPTION_TYPE = Randomized, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256') NOT NULL,
    [Date] [date] NOT NULL, -- to support Date-CheckerID-FullName unique constraint
    [DateTime] [datetime] NOT NULL,
    [Station] [smallint] NOT NULL,
    [Question1] [bit] NOT NULL,
    [Question2] [bit] NOT NULL
) ON [PRIMARY]
GO

SET ANSI_PADDING ON
GO

CREATE UNIQUE CLUSTERED INDEX [UCI_EmployeeTemperature]
ON [dbo].[EmployeeTemperature]
(
    [Date] ASC,
    [CheckerID] ASC,
    [FullName] ASC
)
WITH
(
PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON
) ON [PRIMARY]

GO
_

以下の_Stored Procedure_コードは、暗号化されたデータを取得するために使用されます(最終的には、ASPXシングルページアプリケーション(SPA)Webアプリによって使用されます)。

_--SELECT OBJECT_ID('dbo.sp_GetEmployeeTemps','P') -- debug below
IF OBJECT_ID('dbo.sp_GetEmployeeTemps','P') IS NULL
   EXEC('CREATE PROCEDURE [dbo].[sp_GetEmployeeTemps] AS BEGIN SET NOCOUNT ON; END')
GO

ALTER PROCEDURE [dbo].[sp_GetEmployeeTemps]
AS

SELECT
 ET.[Entry]
,CASE
    WHEN HR.[Employee_ID] IS NOT NULL THEN 'E'
  ELSE 'V'
 END AS [Visitor] -- Show if record is for Employee or Visitor
,ISNULL(HR.[Name],ET.[FullName]) AS [Name] -- ISNULL for visitor. return visitor's name if not an employee.
,ET.[Temp]
,(SELECT DISTINCT chk.[Name] FROM [dbo].[Checker] AS chk INNER JOIN [dbo].[EmployeeTemperature] ON ET.[CheckerID] = chk.[LoginID]) AS [Checker]
,CAST(FORMAT(ET.[DateTime], 'yyyy-MM-dd hh:mm:ss', 'en-US') AS DATETIME) AS [Time] -- so that it doesn't round seconds to minutes (converting to SMALLDATETIME does that) and shows to the second.
,CASE
    WHEN ET.[Question1] = 1 THEN 'Yes'
    WHEN ET.[Question1] = 0 THEN 'No' 
 ELSE NULL
 END AS [Question1]
,CASE
    WHEN ET.[Question2] = 1 THEN 'Yes'
    WHEN ET.[Question2] = 0 THEN 'No' 
 ELSE NULL
 END AS [Question2]
FROM [dbo].[vw_Employees] AS HR
FULL JOIN -- to allow Visitors to be retrieved
(
    SELECT
     [Entry]
    ,[Temp]
    ,[CheckerID]
    ,[FullName]
    ,[EmployeeID]
    ,[DateTime]
    ,[Question1]
    ,[Question2]
    FROM [dbo].[EmployeeTemperature]
    WHERE CONVERT(DATE, [DateTime]) = CONVERT(DATE, GETDATE())
) AS ET
ON HR.[Employee_ID] = ET.[EmployeeID] -- encrypted
WHERE ET.[Entry] IS NOT NULL -- to not show unchecked employees.

GO

EXEC sp_refresh_parameter_encryption 'dbo.sp_GetEmployeeTemps';
_

上記の手順を作成または変更しようとすると、次のエラーが表示されます。

The data types char and char(10) encrypted with (encryption_type = 'DETERMINISTIC', encryption_algorithm_name = 'AEAD_AES_256_CBC_HMAC_SHA_256', column_encryption_key_name = 'CEK_Auto1', column_encryption_key_database_name = 'Employee_Temperature') collation_name = 'Latin1_General_BIN2' are incompatible in the equal to operator.

これは、問題がこのJOIN句にあることを示しているようです:

_ON HR.[Employee_ID] = ET.[EmployeeID] -- encrypted_

この結合では、_HR.[Employee_ID]_は暗号化されておらず、_[vw_Employees]_ビューの一部であり、_ET.[EmployeeID]_は暗号化された列です。

この等価結合が機能しないのはなぜですか?ドキュメントには、暗号化された列を等価結合で使用できると記載されていますが、これは明らかにそうです。

それが問題#1です。


問題#2は、暗号化された列_ET.[FullName]_を含むISNULLにあるようです。

その結合をコメントアウトして、デバッグ目的で_ON 1 = 1_を実行すると、追加のエラーが発生します。

Operand type clash: varchar(50) encrypted with (encryption_type = 'DETERMINISTIC', encryption_algorithm_name = 'AEAD_AES_256_CBC_HMAC_SHA_256', column_encryption_key_name = 'CEK_Auto1', column_encryption_key_database_name = 'Employee_Temperature') collation_name = 'Latin1_General_BIN2' is incompatible with varchar

そのシナリオを処理するための提案はありますか?

MSドキュメントをGitHubの例で更新するように要求しました: https://github.com/MicrosoftDocs/sql-docs/issues/4550

3

この結合では、HR。[Employee_ID]は暗号化されておらず、[vw_Employees]ビューの一部であり、ET。[EmployeeID]は暗号化された列です。

この等価結合が機能しないのはなぜですか?ドキュメントには、暗号化された列を等価結合で使用できると記載されていますが、これは明らかにそうです。

ドキュメントを詳しく見てください:

確定的暗号化では、特定のプレーンテキスト値に対して常に同じ暗号化値が生成されます。

確定的暗号化を使用すると、ポイントルックアップ、等価結合、グループ化、およびインデックス作成暗号化された列が可能になります。

(Emphasis mine)Always Encryptedの基本的な使用例を思い出してください:

Always Encryptedを使用すると、クライアントはクライアントアプリケーション内の機密データを暗号化でき、暗号化キーをデータベースエンジンに公開することはできません...

エンジンが暗号化されていない値を認識しない場合、結合で暗号化されていない値と暗号化されている値をどのように比較できますか?

静的入力に対して同じ暗号化された値を取得するため、確定的暗号化でルックアップ、結合などを実行できます。ただし、暗号化されたものと暗号化されていないものを比較できるとはどこにも触れていません。

あなたのケースでは、暗号化された列で一致を見つけるために検索キーを暗号化する必要があります。これは確定的であるため、それらが同じ開始値である場合は、暗号化された値と一致させることができます。

TL; DR-確定的な暗号化された列を確定的な暗号化された列に結合することは問題ありませんが、暗号化されていない列を暗号化された列に結合することはできません。

1
LowlyDBA