Запутать / Маска / Scramble личная информация - PullRequest
9 голосов
/ 04 октября 2008

Я ищу доморощенный способ шифрования производственных данных для использования при разработке и тестировании. Я создал пару сценариев, которые делают случайные номера социального страхования, меняют даты рождения, шифруют электронные письма и т. Д. Но я натолкнулся на стену, пытаясь зашифровать имена клиентов. Я хочу сохранить настоящие имена, чтобы мы могли по-прежнему использовать или искать, чтобы исключить случайную генерацию букв. До сих пор я пытался создать временную таблицу со всеми фамилиями в таблице, а затем обновить таблицу клиентов случайным выбором из временной таблицы. Как это:

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)

Это хорошо работало в тесте, но полностью справлялось с большими объемами данных (> 20 минут для строк по 40 КБ)

Все это, чтобы спросить, как бы вы зашифровали имена клиентов, сохраняя при этом настоящие имена и вес производственных данных?

ОБНОВЛЕНИЕ: Никогда не подводит, вы пытаетесь поместить всю информацию в пост, и вы забыли что-то важное. Эти данные также будут использоваться в наших коммерческих и демонстрационных средах, которые являются общедоступными. Некоторые из ответов - это то, что я пытаюсь сделать, чтобы «переключать» имена, но мой вопрос буквально, как кодировать в T-SQL?

Ответы [ 12 ]

3 голосов
/ 04 октября 2008

Я использую Генерируемые данные . Это PHP-скрипт с открытым исходным кодом, который может генерировать всевозможные фиктивные данные.

1 голос
/ 04 октября 2008

При этом я обычно пишу небольшую программу, которая сначала загружает много имен и фамилий в два массива, а затем просто обновляет базу данных, используя случайные имя / фамилию из массивов. Он работает очень быстро даже для очень больших наборов данных (более 200 000 записей)

1 голос
/ 04 октября 2008

Очень простым решением будет ROT13 текст.

Лучший вопрос может быть: почему вы чувствуете необходимость шифровать данные? Если у вас есть ключ шифрования, вы также можете рассмотреть возможность выполнения текста через DES, AES или аналогичные. Однако у Thos могут возникнуть проблемы с производительностью.

0 голосов
/ 26 февраля 2019

Я использую метод, который изменяет символы в имени на другие символы, которые находятся в том же «диапазоне» частоты использования в английских именах. Судя по всему, распределение символов в именах отличается от обычного разговорного английского. Например, «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
;
0 голосов
/ 09 мая 2018

Я столкнулся с той же проблемой и нашел альтернативное решение, которое может работать для других.

Идея состоит в том, чтобы использовать имя 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

0 голосов
/ 30 июня 2017

Вот способ использования ROT47, который является обратимым, и другой, который является случайным. Вы можете добавить ПК к любой из них, чтобы получить ссылку на «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
0 голосов
/ 13 мая 2017

Еще один сайт для создания фигурных поддельных наборов данных с возможностью вывода T-SQL: https://mockaroo.com/

0 голосов
/ 16 августа 2016

У нас сработал следующий подход, скажем, у нас есть 2 таблицы «Клиенты и продукты»:

CREATE FUNCTION [dbo].[GenerateDummyValues]
(
    @dataType varchar(100),
    @currentValue varchar(4000)=NULL
)
RETURNS varchar(4000)
AS
BEGIN
IF @dataType = 'int'
    BEGIN
        Return '0'
    END
ELSE IF @dataType = 'varchar' OR @dataType = 'nvarchar' OR @dataType = 'char' OR @dataType = 'nchar'
    BEGIN
        Return 'AAAA'
    END
ELSE IF @dataType = 'datetime'
    BEGIN
        Return Convert(varchar(2000),GetDate())
    END
-- you can add more checks, add complicated logic etc
Return 'XXX'
END

Вышеприведенная функция поможет генерировать различные данные в зависимости от входящего типа данных.

Теперь для каждого столбца каждой таблицы, в которой нет слова «id», используйте следующий запрос для генерации дополнительных запросов для манипулирования данными:

select 'select ''update '' + TABLE_NAME + '' set '' + COLUMN_NAME + '' = '' +  '''''''' + dbo.GenerateDummyValues( Data_type,'''') + '''''' where id = '' + Convert(varchar(10),Id) from INFORMATION_SCHEMA.COLUMNS, ' + table_name + ' where RIGHT(LOWER(COLUMN_NAME),2) <> ''id'' and TABLE_NAME = '''+ table_name + '''' + ';' from  INFORMATION_SCHEMA.TABLES;

Когда вы выполняете запрос выше, он генерирует запросы на обновление для каждой таблицы и для каждого столбца этой таблицы, например:

select 'update ' + TABLE_NAME + ' set ' + COLUMN_NAME + ' = ' +  '''' + dbo.GenerateDummyValues( Data_type,'') + ''' where id = ' + Convert(varchar(10),Id) from INFORMATION_SCHEMA.COLUMNS, Customers where RIGHT(LOWER(COLUMN_NAME),2) <> 'id' and TABLE_NAME = 'Customers';
select 'update ' + TABLE_NAME + ' set ' + COLUMN_NAME + ' = ' +  '''' + dbo.GenerateDummyValues( Data_type,'') + ''' where id = ' + Convert(varchar(10),Id) from INFORMATION_SCHEMA.COLUMNS, Products where RIGHT(LOWER(COLUMN_NAME),2) <> 'id' and TABLE_NAME = 'Products';

Теперь, когда вы выполните вышеуказанные запросы, вы получите окончательные запросы на обновление, которые обновят данные ваших таблиц.

Вы можете выполнить это в любой базе данных сервера SQL, независимо от того, сколько у вас таблиц, она будет генерировать для вас запросы, которые могут быть выполнены в дальнейшем.

Надеюсь, это поможет.

0 голосов
/ 29 октября 2011

Я сейчас работаю над этим в моей компании - и это оказывается очень хитрой вещью. Вы хотите иметь реалистичные имена, но не должны раскрывать какую-либо реальную личную информацию.

Мой подход состоял в том, чтобы сначала создать случайное «отображение» фамилий на другие фамилии, а затем использовать это отображение для изменения всех фамилий. Это хорошо, если у вас есть дубликаты имен. Предположим, у вас есть 2 записи «Джона Смита», которые представляют одного и того же реального человека. Если вы изменили одну запись на «Джона Адамса», а другую на «Джона Беста», то у вашего одного «человека» теперь есть 2 разных имени! При сопоставлении все вхождения «Смита» изменяются на «Джонс», и поэтому дубликаты (или даже члены семьи) по-прежнему имеют одинаковую фамилию, что делает данные более «реалистичными».

Мне также придется шифровать адреса, номера телефонов, номера банковских счетов и т. Д., И я не уверен, как я подойду к ним. Сохранение «реалистичности» данных во время скремблирования - это, безусловно, глубокая тема. Это должно быть сделано много раз многими компаниями - кто делал это раньше? Что вы узнали?

0 голосов
/ 30 апреля 2009

Вместо этого используйте временную таблицу, и запрос будет очень быстрым. Я просто бегал по 60К строк за 4 секунды. Я буду использовать это в будущем.

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
...