T-SQL中的动态长度混淆方法

bol*_*aly 1 security sql-server t-sql data-masking

我想从 SQL Server 数据库中混淆(打乱)敏感数据,但方式将提供:

  • 不可逆性(明文无法从混淆数据中导出),
  • 混淆后的数据长度需要与混淆前的数据长度相同。
  • 对于相同输入值的重复混淆,混淆值不需要是唯一的。老实说,我更喜欢为可以使用的相同输入获得相同的值(例如,不同表中的一些匹配数据,可能在测试用例中有用)。

例子:

Abc -> zyx (lenght: 3)
StackOverflow -> a65vr4doqjd (lenght: 11)
Run Code Online (Sandbox Code Playgroud)

通常我会避免“自制”算法,所以你知道一些可以提供这种混淆的 MS 内置解决方案吗?

我希望我清楚地表达了我的问题,否则让我知道,我会尝试根据需要添加尽可能多的信息。

Sol*_*zky 6

不,我不知道有任何内置函数可以做到这一点。但是,您仍然可以在不做任何太复杂的事情的情况下完成此操作。

您可以使用内置的CRYPT_GEN_RANDOM函数(在 SQL Server 2008 R2 中引入),它根据提供的长度生成随机值。输出是十六进制/二进制值,因此返回的每个字节都表示为两个字母数字字符(因此是/ 2 + 1下面的部分)。

DECLARE @InputString NVARCHAR(4000) = 'hello';
SELECT SUBSTRING(CONVERT(VARCHAR(8000),
                         CRYPT_GEN_RANDOM((LEN(@InputString) / 2) + 1),
                         2),
                 1,
                 LEN(@InputString)) AS [Obfuscated];

SET @InputString = 'test';
SELECT SUBSTRING(CONVERT(VARCHAR(8000),
                         CRYPT_GEN_RANDOM((LEN(@InputString) / 2) + 1),
                         2),
                 1,
                 LEN(@InputString)) AS [Obfuscated];
Run Code Online (Sandbox Code Playgroud)

返回以下内容:

DECLARE @InputString NVARCHAR(4000) = 'hello';
SELECT SUBSTRING(CONVERT(VARCHAR(8000),
                         CRYPT_GEN_RANDOM((LEN(@InputString) / 2) + 1),
                         2),
                 1,
                 LEN(@InputString)) AS [Obfuscated];

SET @InputString = 'test';
SELECT SUBSTRING(CONVERT(VARCHAR(8000),
                         CRYPT_GEN_RANDOM((LEN(@InputString) / 2) + 1),
                         2),
                 1,
                 LEN(@InputString)) AS [Obfuscated];
Run Code Online (Sandbox Code Playgroud)

这里唯一真正的缺点是这需要内联完成,因为CRYPT_GEN_RANDOM不能在用户定义的函数(UDF:标量或表值)中使用。但是,它仍然可以应用于使用 CTE 的基于集合的方法,如下所示(只需设置@MaxLength为被混淆的列的最大长度):

8C108

9A7A
Run Code Online (Sandbox Code Playgroud)

返回以下内容:

DECLARE @MaxLength INT = 10;

;WITH cte AS
(
    SELECT CONVERT(VARCHAR(8000),
                   CRYPT_GEN_RANDOM((@MaxLength / 2) + 1),
                2) AS [Random]

)
SELECT tmp.[String],
       cte.[Random],
       SUBSTRING(cte.[Random], 1, LEN(tmp.[String])) AS [Obfuscated]
FROM   (VALUES (N'test'), (N'Hello')) tmp(String)
CROSS JOIN  cte;
Run Code Online (Sandbox Code Playgroud)

如您所见,CRYPT_GEN_RANDOM为每一行返回不同的值。

另外,不确定这是否可以接受,但返回的唯一字母字符是A- F


或者,如果您希望混淆对于相同的输入值是可重复的,或者至少不介意它是可重复的并且希望此代码在一个函数中以便更容易应用于多个列,您可以使用HASHBYTES函数,如CRYPT_GEN_RANDOM,返回十六进制/二进制字节。与 不同CRYPT_GEN_RANDOM,输出长度是固定的(在本例中为 64 个字符,因为我使用的是SHA2_256),因此REPLICATE如果输入字符串的长度超过 64 个字符,我曾经重复散列值。也不同于CRYPT_GEN_RANDOM,HASHBYTES 用于用户定义函数 (UDF) :-)。

String    Random          Obfuscated
------    ------------    ----------
test      F99B3888F993    F99B
Hello     D3250E74F0A3    D3250
Run Code Online (Sandbox Code Playgroud)

可以按如下方式使用:

CREATE FUNCTION dbo.Obfuscate(@InputString NVARCHAR(4000))
RETURNS TABLE
WITH SCHEMABINDING
AS RETURN
  SELECT SUBSTRING(REPLICATE(CONVERT(VARCHAR(8000),
                                     HASHBYTES('SHA2_256', @InputString),
                                     2),
                             (LEN(@InputString) / 64) + 1),
                   1,
                   LEN(@InputString)) AS [Obfuscated];
GO
Run Code Online (Sandbox Code Playgroud)

返回以下内容:

SELECT tmp.[String],
       LEN(tmp.[String]) AS [InputLength],
       ob.[Obfuscated],
      LEN(ob.[Obfuscated]) AS [OutputLength]
FROM   (VALUES (N'test'), (N'Hello'), (REPLICATE(N'A', 63)),
               (REPLICATE(N'B', 64)), (REPLICATE(N'C', 65)),
               (REPLICATE(N'D', 4000))) tmp(String)
CROSS APPLY dbo.Obfuscate(tmp.[String]) ob;
Run Code Online (Sandbox Code Playgroud)

请注意:如果您需要超出A- 的字母字符F和/或需要为不同的输入值提供不同的混淆值(即减少冲突的机会),那么上述任一方法都可以很容易地进行调整来做到这一点。

  • @Serg 是的,这两种方法肯定会发生冲突(我刚刚添加了另一种方法),但是 OP 还没有迹象表明冲突会成为问题。如果他们愿意,那么我可以稍作修改,但在认为有必要之前不值得花时间进行修改。 (2认同)