有没有一种快速的方法来查找SQL Server 2008 R2中所有已加密/已加密数据的列?
我需要使开发服务器中所有加密列中的数据无效(根据我们的业务规则)。我知道大多数专栏是因为我们经常使用它们,但是我想透彻一点,也希望能够证明已经找到了所有专栏。
我已经在网上搜索,查看了INFORMATION_SCHEMA并检查了我认为有用的DMV以及sys.columns和sys.objects,但是到目前为止还算不上什么。
有没有一种快速的方法来查找SQL Server 2008 R2中所有已加密/已加密数据的列?
我需要使开发服务器中所有加密列中的数据无效(根据我们的业务规则)。我知道大多数专栏是因为我们经常使用它们,但是我想透彻一点,也希望能够证明已经找到了所有专栏。
我已经在网上搜索,查看了INFORMATION_SCHEMA并检查了我认为有用的DMV以及sys.columns和sys.objects,但是到目前为止还算不上什么。
Answers:
假设您正在谈论使用SQL Server密钥加密的数据,则可以找到这些列。
该Key_name()
函数将针对该特定值返回用于加密的密钥的名称,如果没有使用“已知”密钥(第三方或简单未加密)进行加密的任何内容,则该函数将返回NULL。
有了该Knowlegde,我们可以测试每一列,以查看它是否至少包含一行包含返回键名的varbinary值的行。
key_name()的功能
--create a test database
CREATE DATABASE [Test_ENCR]
GO
--change context
USE [Test_ENCR]
GO
--because it's possible to encrypt different rows with different keys I'll create 2 keys for this demo
-- Create a symmetric key
CREATE SYMMETRIC KEY symmetricKey1
WITH ALGORITHM = AES_128
ENCRYPTION BY PASSWORD = 'password01!';
GO
-- Create a second key
CREATE SYMMETRIC KEY symmetricKey2
WITH ALGORITHM = AES_128
ENCRYPTION BY PASSWORD = 'password02!';
GO
--create a table that will have a column holding:
--1: encrypted row with key1
--2: encrypted row with key2
--3: a non encrypted just varbinary value
CREATE TABLE encryptedTable
(ID int IDENTITY PRIMARY KEY,
EncryptedCol varbinary(256) NOT NULL);
GO
-- open key1
OPEN SYMMETRIC KEY symmetricKey1
DECRYPTION BY PASSWORD = 'password01!';
GO
-- open key2
OPEN SYMMETRIC KEY symmetricKey2
DECRYPTION BY PASSWORD = 'password02!';
GO
--insert encrypted data with key1
INSERT INTO encryptedTable(encryptedCol)
VALUES ( ENCRYPTBYKEY (Key_GUID('symmetricKey1'), 'EncryptedText1'));
GO
--insert encrypted data with key2
INSERT INTO encryptedTable(encryptedCol)
VALUES ( ENCRYPTBYKEY (Key_GUID('symmetricKey2'), 'EncryptedText2'));
GO
--insert just varbinary data
INSERT INTO encryptedTable(encryptedCol)
VALUES (CONVERT(varbinary(256),'NotEncryptedTextJustVarBinary'))
--have a look, without the key, all varbinary for you.
SELECT * FROM encryptedTable
GO
结果:
--Return all key_names
SELECT DISTINCT key_name(encryptedcol),
EncryptedCol
FROM encryptedTable;
结果:
如何实现查找加密列的方法
--How do we dynamically find all the columns that have at least one row with a encrypted value?
-- first we will find all tables and column with a varbinary datatype
-- then we will test all those columns with a simple select
-- If the key_name() function returns a value, the column and table name are stored together with the keyname
--create a table to hold all varbinary columns and tables
CREATE TABLE #TablesWithVarbinCols ( ID int IDENTITY,
TableName nvarchar(128),
ColumnName nvarchar(128)
);
--create a table to hold the end result
CREATE TABLE #TablesWithEncryption (
TableName nvarchar(128),
ColumnName nvarchar(128),
KeyName varchar(128)
);
--find and store all table and column names of user tables containing a varbinary column
INSERT INTO #TablesWithVarbinCols (TableName,ColumnName)
SELECT o.[name] as TableName,
c.[name] as ColumnName
FROM sys.objects o
INNER JOIN sys.columns c
ON o.[object_id]=c.[object_id]
INNER JOIN sys.types t
ON c.system_type_id=t.system_type_id
WHERE o.[type]='U'
AND t.name=N'varbinary'
AND c.max_length > -1;
DECLARE @col nvarchar(256)
DECLARE @tab nvarchar(256)
DECLARE @c int = 1
DECLARE @MaxC int
DECLARE @SQL varchar(max)
SELECT @MaxC=MAX(ID)
FROM #TablesWithVarbinCols
--loop the previous result and create a simple select statement with a key_name() is not null where clause.
--If you have a result, store the details
WHILE @c <= @MaxC
BEGIN
SELECT @Tab=TableName,
@col=ColumnName
FROM #TablesWithVarbinCols
WHERE ID=@c
SET @SQL=' INSERT INTO #TablesWithEncryption (TableName, ColumnName, KeyName)
SELECT DISTINCT '''+@Tab +''',''' +@col +''', key_name('+@Col +') from '+ @tab +'
WHERE key_name('+@Col +') is not null;'
exec (@SQL)
DELETE
FROM #TablesWithVarbinCols
WHERE id=@c;
SET @c=@c+1
END
--select the result
SELECT * FROM #TablesWithEncryption;
结果:
--cleanup
DROP TABLE #TablesWithVarbinCols;
DROP TABLE #TablesWithEncryption;
单元级加密的问题是该列本身并未真正加密,而是该列中包含的数据。列本身只是varbinary列(因为这是必需的),并且可以包含完全清晰的数据。ENCRYPTBY*
和DECRYPTBY*
功能的使用真正使数据加密。
您可以通过简单地在sys.columns视图中查询所有varbinary列开始:
select
object_name(a.object_id) [objectname]
,a.name [columnname]
,a.column_id
from
sys.columns a
join sys.types b on (a.system_type_id = b.system_type_id)
where
b.name = N'varbinary';
否则,您将需要检查您的代码以识别在何处使用加密/解密功能:
select
object_name(object_id) [objectname]
,definition
from
sys.sql_modules
where
definition like N'%ENCRYPT%'
OR definition like N'%DECRYPT%';
在特定的数据库上运行以下查询
select
t.name as [Table],
c.name as [Column],
c.encryption_type_desc
from
sys.all_columns c inner join
sys.tables t on c.object_id = t.object_id
where
c.encryption_type is not null
order by
t.name,
c.name
从略有改变
您可以通过搜索所有varbinary列并使用KEY_NAME函数检查加密密钥来找到用密钥\证书加密的列。
但是,该过程有些昂贵且费时。如果需要定期查找这些列,建议使用扩展属性“标记”这些列。我们可以在Edward Dortland的解决方案的基础上,对带有扩展属性(例如,加密,加密密钥和加密证书)的列进行“标记”。
--create a table to hold all varbinary columns and tables
CREATE TABLE #TablesWithVarbinCols ( ID int IDENTITY,
SchemaName nvarchar(128),
TableName nvarchar(128),
ColumnName nvarchar(128)
);
--find and store all table and column names of user tables containing a
varbinary column
INSERT INTO #TablesWithVarbinCols (SchemaName,TableName,ColumnName)
SELECT s.[name] as SchemaName,
o.[name] as TableName,
c.[name] as ColumnName
FROM sys.objects o
INNER JOIN sys.schemas s
ON s.[schema_id] = o.[schema_id]
INNER JOIN sys.columns c
ON o.[object_id]=c.[object_id]
INNER JOIN sys.types t
ON c.system_type_id=t.system_type_id
WHERE o.[type]='U'
AND t.name=N'varbinary'
AND c.max_length > -1;
DECLARE @sch nvarchar(128)
DECLARE @col nvarchar(256)
DECLARE @tab nvarchar(256)
DECLARE @key nvarchar(256)
DECLARE @cert nvarchar(256)
DECLARE @c int = 1
DECLARE @MaxC int
DECLARE @SQL nvarchar(max)
SELECT @MaxC=MAX(ID)
FROM #TablesWithVarbinCols
--loop the previous result and create a simple select statement with a
key_name() is not null where clause.
--If you have a result, store the details
WHILE @c <= @MaxC
BEGIN
SET @key = NULL;
SELECT @sch=SchemaName,
@Tab=TableName,
@col=ColumnName
FROM #TablesWithVarbinCols
WHERE ID=@c
SET @SQL='SELECT DISTINCT @key= key_name('+@Col +') from '+ @tab +'
WHERE key_name('+@Col +') is not null;'
exec sp_executesql @SQL, N'@key nvarchar(256) out', @key out
SELECT @cert = c.name
from sys.symmetric_keys sk
join sys.key_encryptions ke
on
sk.symmetric_key_id= ke.key_id
join sys.certificates c
on
ke.thumbprint=c.thumbprint
where sk.name = @key
IF (@key IS NOT NULL)
BEGIN
SET @SQL=
'EXEC sp_addextendedproperty @name = N''encrypted'', @value = N''1'', '+
'@level0type = N''Schema'', @level0name = '''+@Sch+''', '+
'@level1type = N''Table'', @level1name = '''+@tab+''','+
'@level2type = N''Column'', @level2name = '''+@col+'''
'
EXEC sp_executesql @SQL
SET @SQL=
'EXEC sp_addextendedproperty @name = N''encryptkey'', @value = '''+@key+''', '+
'@level0type = N''Schema'', @level0name = '''+@Sch+''', '+
'@level1type = N''Table'', @level1name = '''+@tab+''','+
'@level2type = N''Column'', @level2name = '''+@col+'''
'
EXEC sp_executesql @SQL
SET @SQL=
'EXEC sp_addextendedproperty @name = N''encryptcert'', @value = '''+@cert+''', '+
'@level0type = N''Schema'', @level0name = '''+@Sch+''', '+
'@level1type = N''Table'', @level1name = '''+@tab+''','+
'@level2type = N''Column'', @level2name = '''+@col+'''
'
EXEC sp_executesql @SQL
END
DELETE
FROM #TablesWithVarbinCols
WHERE id=@c;
SET @c=@c+1
END
drop table #TablesWithVarbinCols
然后,我们可以通过搜索扩展属性轻松找到加密列。
--Adjust WHERE clause depending on what tags you are looking for
SELECT
SCHEMA_NAME(tbl.schema_id) AS SchemaName,
tbl.name AS TableName,
clmns.name AS ColumnName,
p.name AS ExtendedPropertyName, --remove for programming
CAST(p.value AS sql_variant) AS ExtendedPropertyValue
FROM
sys.tables AS tbl
INNER JOIN sys.all_columns AS clmns ON clmns.object_id=tbl.object_id
INNER JOIN sys.extended_properties AS p ON p.major_id=tbl.object_id AND p.minor_id=clmns.column_id AND p.class=1
WHERE p.name in ('encrypted','encryptkey','encryptcert')