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
この結合では、HR。[Employee_ID]は暗号化されておらず、[vw_Employees]ビューの一部であり、ET。[EmployeeID]は暗号化された列です。
この等価結合が機能しないのはなぜですか?ドキュメントには、暗号化された列を等価結合で使用できると記載されていますが、これは明らかにそうです。
ドキュメントを詳しく見てください:
確定的暗号化では、特定のプレーンテキスト値に対して常に同じ暗号化値が生成されます。
確定的暗号化を使用すると、ポイントルックアップ、等価結合、グループ化、およびインデックス作成暗号化された列が可能になります。
(Emphasis mine)Always Encryptedの基本的な使用例を思い出してください:
Always Encryptedを使用すると、クライアントはクライアントアプリケーション内の機密データを暗号化でき、暗号化キーをデータベースエンジンに公開することはできません...
エンジンが暗号化されていない値を認識しない場合、結合で暗号化されていない値と暗号化されている値をどのように比較できますか?
静的入力に対して同じ暗号化された値を取得するため、確定的暗号化でルックアップ、結合などを実行できます。ただし、暗号化されたものと暗号化されていないものを比較できるとはどこにも触れていません。
あなたのケースでは、暗号化された列で一致を見つけるために検索キーを暗号化する必要があります。これは確定的であるため、それらが同じ開始値である場合は、暗号化された値と一致させることができます。
TL; DR-確定的な暗号化された列を確定的な暗号化された列に結合することは問題ありませんが、暗号化されていない列を暗号化された列に結合することはできません。