Obfuscate / Mask / Scramble个人信息

时间:2008-10-03 21:00:31

标签: sql sql-server tsql privacy scramble

我正在寻找一种本土的方式来加扰生产数据以用于开发和测试。我已经建立了几个脚本,可以随机编制社会安全号码,转移出生日期,争夺电子邮件等等。但是我遇到了试图扰乱客户名称的隔离墙。我想保留真实姓名,这样我们仍然可以使用或搜索随机字母生成。到目前为止,我所尝试的是构建表中所有姓氏的临时表,然后使用临时表中的随机选择更新客户表。像这样:

DECLARE @Names TABLE (Id int IDENTITY(1,1),[Name] varchar(100))

/* Scramble the last names (randomly pick another last name) */
INSERT @Names SELECT LastName FROM Customer ORDER BY NEWID();
WITH [Customer ORDERED BY ROWID] AS
(SELECT ROW_NUMBER() OVER (ORDER BY NEWID()) AS ROWID, LastName FROM Customer)
UPDATE [Customer ORDERED BY ROWID] SET LastName=(SELECT [Name] FROM @Names WHERE ROWID=Id)

这在测试中效果很好,但完全陷入困境处理大量数据(对于40K行,大约20分钟)

所有这些问题,您如何在保留真实姓名和生产数据的重量的同时扰乱客户名称?

更新:永远不会失败,您尝试将所有信息都放在帖子中,而您会忘记重要的事情。这些数据也将用于我们的销售和销售。公开可用的演示环境。一些答案是我试图做的,“切换”名称,但我的问题是字面意思,如何在T-SQL中编码?

11 个答案:

答案 0 :(得分:3)

我使用generatedata。它是一个开源的PHP脚本,可以生成各种虚拟数据。

答案 1 :(得分:1)

一个非常简单的解决方案是ROT13文本。

更好的问题可能是您认为需要加扰数据的原因?如果您有加密密钥,还可以考虑通过DES或AES或类似方式运行文本。但是,这会产生潜在的性能问题。

答案 2 :(得分:1)

在做类似的事情时,我通常会编写一个小程序,首先在两个数组中加载很多名称和姓氏,然后使用数组中的随机名称/姓氏更新数据库。即使对于非常大的数据集(200.000+记录),它的工作速度也非常快

答案 3 :(得分:0)

为什么不使用某种Random Name Generator?

答案 4 :(得分:0)

使用临时表,查询速度非常快。我只是在4秒内跑了60K行。我将继续使用这个。

DECLARE TABLE #Names 
(Id int IDENTITY(1,1),[Name] varchar(100))

/ *扰乱姓氏(随机选择另一个姓氏)* /

INSERT #Names
  SELECT LastName 
  FROM Customer 
  ORDER BY NEWID();
WITH [Customer ORDERED BY ROWID] AS
(SELECT ROW_NUMBER() OVER (ORDER BY NEWID()) AS ROWID, LastName FROM Customer)

UPDATE [Customer ORDERED BY ROWID] 

SET LastName=(SELECT [Name] FROM #Names WHERE ROWID=Id)

DROP TABLE #Names

答案 5 :(得分:0)

我现在正在我的公司工作 - 事实证明这是一件非常棘手的事情。您希望名称具有真实性,但不得泄露任何真实的个人信息。

我的方法是首先创建一个随机的“姓氏映射”到其他姓氏,然后使用该映射来更改所有姓氏。如果您有重复的名称记录,这很好。假设您有2个“John Smith”记录,它们都代表同一个真人。如果您将一条记录更改为“John Adams”而将另一条记录更改为“John Best”,那么您的一个“人”现在有两个不同的名称!通过映射,所有出现的“Smith”都会变为“Jones”,因此重复(甚至是家庭成员)仍然会使用相同的姓氏,从而使数据更加“逼真”。

我还必须加扰地址,电话号码,银行帐号等......我不确定如何处理这些问题。在加扰时保持数据“真实”当然是一个深刻的话题。许多公司必须多次这样做 - 以前这么做过吗?你学到了什么?

答案 6 :(得分:0)

生成异形假数据集的另一个站点,带有T-SQL输出选项: https://mockaroo.com/

答案 7 :(得分:0)

这是一种使用ROT47的方法,它是可逆的,另一种是随机的。您可以添加PK以链接回“un scrambled”版本

declare @table table (ID int, PLAIN_TEXT nvarchar(4000))
insert into @table
values
(1,N'Some Dudes name'),
(2,N'Another Person Name'),
(3,N'Yet Another Name')

--split your string into a column, and compute the decimal value (N) 
if object_id('tempdb..#staging') is not null drop table #staging
select 
    substring(a.b, v.number+1, 1) as Val
    ,ascii(substring(a.b, v.number+1, 1)) as N
    --,dense_rank() over (order by b) as RN
    ,a.ID
into #staging
from (select PLAIN_TEXT b, ID FROM @table) a
    inner join
         master..spt_values v on v.number < len(a.b)
where v.type = 'P' 

--select * from #staging


--create a fast tally table of numbers to be used to build the ROT-47 table.

;WITH
    E1(N) AS (select 1 from (values (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))dt(n)),
    E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
    E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
    cteTally(N) AS 
    (
        SELECT  ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
    )



--Here we put it all together with stuff and FOR XML
select 
    PLAIN_TEXT
    ,ENCRYPTED_TEXT =
        stuff((
        select
            --s.Val
            --,s.N
            e.ENCRYPTED_TEXT
        from #staging s
        left join(
        select 
            N as DECIMAL_VALUE
            ,char(N) as ASCII_VALUE
            ,case 
                when 47 + N <= 126 then char(47 + N)
                when 47 + N > 126 then char(N-47)
            end as ENCRYPTED_TEXT
        from cteTally
        where N between 33 and 126) e on e.DECIMAL_VALUE = s.N
        where s.ID = t.ID
        FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 0, '')
from @table t


--or if you want really random
select 
    PLAIN_TEXT
    ,ENCRYPTED_TEXT =
        stuff((
        select
            --s.Val
            --,s.N
            e.ENCRYPTED_TEXT
         from #staging s
        left join(
        select 
            N as DECIMAL_VALUE
            ,char(N) as ASCII_VALUE
            ,char((select ROUND(((122 - N -1) * RAND() + N), 0))) as ENCRYPTED_TEXT
        from cteTally
        where (N between 65 and 122) and N not in (91,92,93,94,95,96)) e on e.DECIMAL_VALUE = s.N
        where s.ID = t.ID
        FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 0, '')
from @table t

答案 8 :(得分:0)

我自己遇到了同样的问题,并想出了一个可能适用于其他人的替代解决方案。

我们的想法是在名称上使用MD5,然后将其最后3位十六进制数字映射到名称表中。您可以单独为名字和姓氏执行此操作。

3个十六进制数字表示从0到4095的小数,所以我们需要一个4096个名字和4096个姓氏的列表。

所以conv(substr(md5(first_name), 3),16,10)(在MySQL语法中)将是一个从0到4095的索引,可以与一个包含4096个名字的表连接。同样的概念可以应用于姓氏。

使用MD5(而不是随机数)可确保原始数据中的名称始终映射到测试数据中的相同名称。

您可以在此处获取名称列表:

https://gist.github.com/elifiner/cc90fdd387449158829515782936a9a4

答案 9 :(得分:0)

我使用一种方法来将名称中的字符更改为英语名称中使用频率在相同“范围”内的其他字符。显然,姓名中字符的分布与普通会话英语中的分布不同。例如,“ x”和“ z”出现的时间为0.245%,因此它们被交换了。另一个极端是,“ w”的使用时间为5.5%,“ s”为6.86%,“ t”为15.978%。我将“ s”更改为“ w”,将“ t”更改为“ s”,将“ w”更改为“ t”。 我将元音“ aeio”放在一个单独的组中,这样一个元音只能被另一个元音代替。类似地,“ q”,“ u”和“ y”根本不被替换。我的分组和决定完全是主观的。

我最终得到了7个不同的“组”,每组2-5个字符,主要基于频率。每个组中的字符会与同一组中的其他字符交换。

最终结果是名称看起来像可能是名称,但来自“不在此处”。

Original name     Morphed name
Loren             Nimag
Juanita           Kuogewso
Tennyson          Saggywig
David             Mijsm
Julie             Kunewa

这是我使用的SQL,其中包括“ TitleCase”功能。根据我在网上发现的字母出现频率的不同,“变形”名称有2种不同的版本。

--    from     https://stackoverflow.com/a/28712621

-- Convert and return param as Title Case

CREATE FUNCTION [dbo].[fnConvert_TitleCase] (@InputString VARCHAR(4000) )
RETURNS VARCHAR(4000)AS
BEGIN
DECLARE @Index INT
DECLARE @Char CHAR(1)
DECLARE @OutputString VARCHAR(255)

SET @OutputString = LOWER(@InputString)
SET @Index = 2
SET @OutputString = STUFF(@OutputString, 1, 1,UPPER(SUBSTRING(@InputString,1,1)))

WHILE @Index <= LEN(@InputString)
BEGIN
   SET @Char = SUBSTRING(@InputString, @Index, 1)
   IF @Char IN (' ', ';', ':', '!', '?', ',', '.', '_', '-', '/', '&','''','(','{','[','@')
      IF @Index + 1 <= LEN(@InputString)
      BEGIN
         IF @Char != ''''  OR  UPPER(SUBSTRING(@InputString, @Index + 1, 1)) != 'S'
            SET @OutputString = STUFF(@OutputString, @Index + 1, 1,UPPER(SUBSTRING(@InputString, @Index + 1, 1)))
      END
         SET @Index = @Index + 1
      END

   RETURN ISNULL(@OutputString,'')

END
Go

--    00.045 x 0.045%
--    00.045 z 0.045%
--
--    Replace(Replace(Replace(TS_NAME,'x','#'),'z','x'),'#','z')
--
--    00.456 k 0.456%
--    00.511 j 0.511%
--    00.824 v 0.824%
--    kjv
--    Replace(Replace(Replace(Replace(TS_NAME,'k','#'),'j','k'),'v','j'),'#','v')
--
--    01.642 g 1.642%
--    02.284 n 2.284%
--    02.415 l 2.415%
--    gnl
--    Replace(Replace(Replace(Replace(TS_NAME,'g','#'),'n','g'),'l','n'),'#','l')
--
--    02.826 r 2.826%
--    03.174 d 3.174%
--    03.826 m 3.826%
--    rdm
--    Replace(Replace(Replace(Replace(TS_NAME,'r','#'),'d','r'),'m','d'),'#','m')
--
--    04.027 f 4.027%
--    04.200 h 4.200%
--    04.319 p 4.319%
--    04.434 b 4.434%
--    05.238 c 5.238%
--    fhpbc
--    Replace(Replace(Replace(Replace(Replace(Replace(TS_NAME,'f','#'),'h','f'),'p','h'),'b','p'),'c','b'),'#','c')
--
--    05.497 w 5.497%
--    06.686 s 6.686%
--    15.978 t 15.978%
--    wst
--    Replace(Replace(Replace(Replace(TS_NAME,'w','#'),'s','w'),'t','s'),'#','t')
--
--
--    02.799 e 2.799%
--    07.294 i 7.294%
--    07.631 o 7.631%
--    11.682 a 11.682%
--    eioa
--    Replace(Replace(Replace(Replace(Replace(TS_NAME,'e','#'),'i','ew'),'o','i'),'a','o'),'#','a')
--
--    -- dont replace
--    00.222 q 0.222%
--    00.763 y 0.763%
--    01.183 u 1.183%

-- Obfuscate a name
Select
   ts_id,
   Cast(ts_name as varchar(42)) as [Original Name]

   Cast(dbo.fnConvert_TitleCase(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(TS_NAME,'x','#'),'z','x'),'#','z'),'k','#'),'j','k'),'v','j'),'#','v'),'g','#'),'n','g'),'l','n'),'#','l'),'r','#'),'d','r'),'m','d'),'#','m'),'f','#'),'h','f'),'p','h'),'b','p'),'c','b'),'#','c'),'w','#'),'s','w'),'t','s'),'#','t'),'e','#'),'i','ew'),'o','i'),'a','o'),'#','a')) as VarChar(42)) As [morphed name] ,
   Cast(dbo.fnConvert_TitleCase(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(TS_NAME,'e','t'),'~','e'),'t','~'),'a','o'),'~','a'),'o','~'),'i','n'),'~','i'),'n','~'),'s','h'),'~','s'),'h','r'),'r','~'),'d','l'),'~','d'),'l','~'),'m','w'),'~','m'),'w','f'),'f','~'),'g','y'),'~','g'),'y','p'),'p','~'),'b','v'),'~','b'),'v','k'),'k','~'),'x','~'),'j','x'),'~','j')) as VarChar(42)) As [morphed name2]

From
   ts_users
;

答案 10 :(得分:-1)

坦率地说,我不确定为什么需要这样做。您的开发/测试环境应该是私有的,位于防火墙后面,不能通过Web访问。

您的开发人员应该受到信任,并且如果他们未能履行您的信任,您就可以获得法律追索权。

我认为真正的问题应该是“我应该加扰数据吗?”,答案是(在我看来)“不”。

如果您出于某种原因将其发送到异地,或者您必须让您的环境可以通过网络访问,或者您是偏执狂,我会实施随机切换。不是构建临时表,而是在每个位置和表中的随机行之间运行切换,一次交换一个数据。

最终结果将是一个包含所有相同数据的表,但随机重组。我相信它也应该比临时表更快。

在SQL中实现Fisher-Yates Shuffle应该很简单...或至少在读取数据库并写入目标的控制台应用程序中。

编辑(2):关闭T-SQL中的袖带答案:

声明@name varchar(50) set @name =(从personID =(随机id号)的人中选择lastName) 更新人 set lastname = @name WHERE personID =(当前行的人员ID)

将其包裹在循环中,并按照Fisher-Yates的指导原则修改随机值约束,然后设置。