Типы данных

Для того, чтобы выполнить анонимизацию базы 1С необходимо понять какие поля могут содержать чувствительную информацию. 

В типичной базе 1С примерно следующее распределение типов полей:

Тип поляПроцент
bytea45
numeric25
mvarchar11
boolean10
timestamp without time zone7
integer2
bigint<0.01
text<0.01

Давай рассмотрим какая информация обычно хранится в этих типах, чтобы понять какие из них имеет смысла анонимизировать.

Тип mvarchar (Строка)

Данный тип встречается во всех полях с типом "Строка", т.е. почти в каждой таблице базы данных.

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

Тип timestamp (Дата)

Поля данного типа встречаются  во всех полях с типом "Число", как правило это следующие поля базы данных 1С:

  • Поле "Дата" в документах
  • Поле "Период" в регистрах накопления, бухгалтерии, сведений
  • Поля "ПериодРегистрации", "ПериодДействия*", "БазовыйПериод*" в регистрах расчета
  • Любое поле с типом "Дата" в любом объекте метаданных

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

Тип numeric (Число)

Данный тип встречается во всех полях с типом "Число", т.е. почти в каждой таблице базы данных.

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

Например, служебные поля платформы часто имеют именно данные тип, например:

  • _lineno - Номер строки в табличной части
  • _sentno, _receivedno - номер отправленного и полученного сообщения в планах обмена
  • _enumorder - поле "Порядок" в перечислениях
  • ОбластьДанныхОсновныеДанные - поле разделителя областей данных
  • _dimhash, _splitter, _edhashdt, _edhashct, _kind, _recordkind - служебные поля регистров.
  • _messageno - номер сообщения в таблицах регистрации изменений
  • _usersworkhistory._urlhash - Хеш по URL в истории работы пользователей
  • v8users.ussprh - Числовое хеш-значение совокупности значений разделителей

и т.д. 

Тип bytea (*Ссылка, хранилище значения)

Данный тип не нужно анонимизировать, потому что он встречается в ссылочных полях, и в случае их анонимизации будет нарушена ссылочная целостность базы.

Тип boolean (Булево)

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

Анонимизация

Универсальный мета-словарь для 1С

Данный мета-словарь разработан и опробован на типовой базе ERP производственной организации.

{
	   "field": {                # Данные поля будут анонимизированы без сканирования
        "rules": [],
        "constants": []
    },
		"skip_rules": [{
            "schema": "public",
            "table": "_inforg92591x1"  # Версии подсистем самописные
        },
        {
            "schema": "public",
            "table": "_inforg36738"  # Версии подсистем
        },
        {
            "schema": "public",
            "table": "v8users"  # Исключаем служебные таблицы 1С
        },
        {
            "schema": "public",
            "table": "_usersworkhistory"  # Исключаем служебные таблицы 1С
        },
        {
            "schema": "public",
            "table_mask": "^_scheduledjobs\\d+$"  # Исключаем служебные таблицы 1С
        },
          {
            "schema": "public",
            "table_mask": "^_datahistory.*"  # Исключаем служебные таблицы 1С
        },
        {
            "schema": "public",
            "table_mask": "^_.*settings$"  # Исключаем служебные таблицы 1С
        },
        {
            "schema": "public",
            "fields": ["_dimhash", "_enumorder "]  # пример исключения конкретных полей
        }],
     "include_rules": [],
	"data_regex": {
		"rules": [
			"[A-Za-z0-9]+([._-][A-Za-z0-9]+)*@[A-Za-z0-9-]+(\.[A-Za-z]{2,})+",  # email
			"^(7?\d{10})$",								# phone 7XXXXXXXXXX
			"^\+7 \(\d{3}\) \d{3}-\d{2}-\d{2}$",		# phone +7 (XXX) XXX-XX-XX
			"^\d{3}-\d{3}-\d{3} \d{2}$",				# СНИЛС XXX-XXX-XXX XX
			"^\d{2}:\d{2}:\d{1,}:\d{1,}$",				# кадастровый номер АА:ВВ:CCCCСCC:КК
			"^[А-Я]{1}[0-9]{3}[А-Я]{2}[0-9]{2,3}$",		# гос номер авто A111BB777
			"^[A-HJ-NPR-Z0-9]{17}$",					# VIN номер авто (не включает буквы I, O и Q для предотвращения путаницы)
			"^Представление=.*$", 						# Для некоторых полей представлений - все равно не нашло
			"^\d{1,3}[.]\d{1,3}[.]\d{1,3}[.]\d{1,3}$",	# IPV4 адреса
			"^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$",    # Даты в формате 1C - YYYY-MM-DD HH:MM:SS
			"^(?:5[1-5][0-9]{2}|222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[01][0-9]|2720)[0-9]{12}$",  												# MasterCard карты 5258704108753590 
            "\b([4]\d{3}[\s]\d{4}[\s]\d{4}[\s]\d{4}|[4]\d{3}[-]\d{4}[-]\d{4}[-]\d{4}|[4]\d{3}[.]\d{4}[.]\d{4}[.]\d{4}|[4]\d{3}\d{4}\d{4}\d{4})\b",  # Visa card карты 4563-7568-5698-4587
            "[0-9]{4}-[0-9]{4}-[0-9]{4}-[0-9]{4}", 		# Любая карта
           	"""(?i)\b((?:[a-z][\w-]+:(?:\/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()]+|\(([^\s()]+|(\([^\s()]+\)))*\))+(?:\(([^\s()]+|(\([^\s()]+\)))*\)|[^\s`!()\[\]{};:'".,?«»“”‘’]))""",  # URLs 
            "^[a-z0-9.-]+\.[a-z]{2,6}$",  				# URL в коротком формате - abc.ru
			"^(\d{10}|\d{12}|\d{13})$",  				# ИНН - 10 цифр для физлиц и ИП, 12 цифр для юрлиц. ОГРН - 13 цифр.
			
		]
	},
	  "data_const": {
        "constants": [
			"company",
			"организация",
			"компания",
			"морозов",
			"иванов",
			"кузнецов",
			"михаил",
			"сергей",
			"елена",
		],
         "partial_constants": [
            "алекс",
            "рович",       
            "кузнец",
            "виктор",
            "ооо",
            "моск",
            "росси",
            "област",
            "обществ",
            "улиц",
            "иван",
            "петр",
			"представление", # Представление контактной информации
			"паспорт", # Представление паспорта  
			"aleks",
			"ivan",
			"petr",
			"victor",
        ]
    },
    "data_func": {},
	"sens_pg_types": [
		"mvarchar"
	],
	"funcs": {
		"mvarchar": "anon_funcs.digest(\"%s\"::text, 'salt_word', 'md5')"
	}
}
CODE

Давайте разберем данный шаблон.

Поля, которые будут анонимизированы без сканирования

"field": {                # Данные поля будут анонимизированы без сканирования
        "rules": [],
        "constants": []
    }
CODE

В данном блоке кода можно явно указать имена полей (constants) или регулярные выражения (rules) для поиска полей, которые будут анонимизированы принудительно.

Условия исключения

В данной секции (skip_rules) мы указываем правила, по которым хотим исключить определенные поля и таблицы из анонимизации. Т.е. мы точно знаем, что это анонимизировать не нужно.

Примеры.

Пример 1. Явно исключаем типовой регистр сведений "Версии подсистем", т.к. версии подсистем попадают под маску IP-адресов. Если их анонимизировать, то вместо версий подсистемы мы получим значение, которое при запуске базы приведет к тому, что механизмы БСП не смогут определить версии подсистем и база просто не запустится после рестора.

{
            "schema": "public",
            "table": "_inforg92591x1"  # Версии подсистем самописные
        },
        {
            "schema": "public",
            "table": "_inforg36738"  # Версии подсистем
        },
CODE

Вам необходимо определить имена данных таблиц и вставить их в данную секцию. В рассматриваемой базе ERP таких регистров сведений было 2, один из них самописный.

Пример 2. Исключаем служебные таблицы платформы 1С, такие как пользователи, история пользователей и другие. В случае их анонимизации база может не запустится после рестора.

{
            "schema": "public",
            "table": "v8users"  # Исключаем служебные таблицы 1С
        },
        {
            "schema": "public",
            "table": "_usersworkhistory"  # Исключаем служебные таблицы 1С
        },
        {
            "schema": "public",
            "table_mask": "^_scheduledjobs\\d+$"  # Исключаем служебные таблицы 1С
        },
          {
            "schema": "public",
            "table_mask": "^_datahistory.*"  # Исключаем служебные таблицы 1С
        },
        {
            "schema": "public",
            "table_mask": "^_.*settings$"  # Исключаем служебные таблицы 1С
        }
CODE

В поле "table_mask" мы указываем регулярное выражение для поиска таблиц, в поле "table" указываем точное имя таблицы.

Пример 3. Как исключить конкретные поля из анонимизации:

{
            "schema": "public",
            "fields": ["_dimhash", "_enumorder "]  # пример исключения конкретных полей
        }
CODE

Условия включения

Секция "include_rules" работает по аналогии с секцией "skip_rules", только она не исключает таблицы, а определяет таблицы, которые подлежат применению дальнейших правил анонимизации. Мы ее не заполняем, т.к. в каждой базе 1С будут уникальные имена таблиц, которые содержат чувствительные данные.

Регулярные выражения

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

 	"data_regex": {
		"rules": [
			"[A-Za-z0-9]+([._-][A-Za-z0-9]+)*@[A-Za-z0-9-]+(\.[A-Za-z]{2,})+",  # email
			"^(7?\d{10})$",								# phone 7XXXXXXXXXX
			"^\+7 \(\d{3}\) \d{3}-\d{2}-\d{2}$",		# phone +7 (XXX) XXX-XX-XX
			"^\d{3}-\d{3}-\d{3} \d{2}$",				# СНИЛС XXX-XXX-XXX XX
			"^\d{2}:\d{2}:\d{1,}:\d{1,}$",				# кадастровый номер АА:ВВ:CCCCСCC:КК
			"^[А-Я]{1}[0-9]{3}[А-Я]{2}[0-9]{2,3}$",		# гос номер авто A111BB777
			"^[A-HJ-NPR-Z0-9]{17}$",					# VIN номер авто (не включает буквы I, O и Q для предотвращения путаницы)
			"^Представление=.*$", 						# Для некоторых полей представлений - все равно не нашло
			"^\d{1,3}[.]\d{1,3}[.]\d{1,3}[.]\d{1,3}$",	# IPV4 адреса
			"^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$",    # Даты в формате 1C - YYYY-MM-DD HH:MM:SS
			"^(?:5[1-5][0-9]{2}|222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[01][0-9]|2720)[0-9]{12}$",  												# MasterCard карты 5258704108753590 
            "\b([4]\d{3}[\s]\d{4}[\s]\d{4}[\s]\d{4}|[4]\d{3}[-]\d{4}[-]\d{4}[-]\d{4}|[4]\d{3}[.]\d{4}[.]\d{4}[.]\d{4}|[4]\d{3}\d{4}\d{4}\d{4})\b",  # Visa card карты 4563-7568-5698-4587
            "[0-9]{4}-[0-9]{4}-[0-9]{4}-[0-9]{4}", 		# Любая карта
           	"""(?i)\b((?:[a-z][\w-]+:(?:\/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()]+|\(([^\s()]+|(\([^\s()]+\)))*\))+(?:\(([^\s()]+|(\([^\s()]+\)))*\)|[^\s`!()\[\]{};:'".,?«»“”‘’]))""",  # URLs 
            "^[a-z0-9.-]+\.[a-z]{2,6}$",  				# URL в коротком формате - abc.ru
			"^(\d{10}|\d{12}|\d{13})$",  				# ИНН - 10 цифр для физлиц и ИП, 12 цифр для юрлиц. ОГРН - 13 цифр.
			
		]
	}
CODE

Константы

Данный раздел мета-словаря содержит перечень предопределенных значений. Если такое значение встретится в поле при поиске сенситивных данных, то оно считается сенситивным.

В подразделе constants указываются искомые значения целиком, например, константа "company" посчитает поле сенситивным, если в нем будет значение "company first", но не посчитает сенситивным поле со значением "companyfirst".

В подразделе partial_constants указывается часть искомого значения, например, частичная константа "compan" посчитает сенситивным как поле со значением "company first", так и со значением "companyfirst".

   "data_const": {
        "constants": [
			"company",
			"организация",
			"компания",
			"морозов",
			"иванов",
			"кузнецов",
			"михаил",
			"сергей",
			"елена",
		],
         "partial_constants": [
            "алекс",
            "рович",       
            "кузнец",
            "виктор",
            "ооо",
            "моск",
            "росси",
            "област",
            "обществ",
            "улиц",
            "иван",
            "петр",
			"представление", # Представление контактной информации
			"паспорт", # Представление паспорта  
			"aleks",
			"ivan",
			"petr",
			"victor",
        ]
    }
CODE

Произвольные функции

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

   "data_func": {}
CODE

Сенсетивные типы данных

В этом разделе указывается перечень типов, которые будут использованы при поиске сенситивных данных. Например, мы указываем "mvarchar", это означает что будет поиск сенситивных данных только в полях, которые имеют тип mvarchar.

sens_pg_types": [
		"mvarchar"
	]
CODE

Функции анонимизации

В данном разделе указывается соответствие какую функцию анонимизации используем для полей с таким типом данных.

"funcs": {
		"mvarchar": "anon_funcs.digest(\"%s\"::text, 'salt_word', 'md5')"
	}
CODE

Выполнение анонимизации 

Поддерживается 2 варианта выполнения:

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

  1. Задать мета-словарь, скорректировать при необходимости под особенности вашей базы данных.
  2. Произвести этап разведки и получить на его выходе сенситивный словарь
  3. Проверить по сенситивному словарю, что все поля с чувствительными данными попали в него. 
  4. Выполнить дамп базы по  сенситивному словарю
  5. Полученный дамп базы передать заказчику анонимизации базы. 

Ответы на часто задаваемые вопросы

Как мне анонимизировать все коды справочников в базе?

Поле "Код" в базах 1С может быть как числового, так и строкового типа, поэтому в разделе типов нужно прописать оба типа:

"sens_pg_types": [
		"mvarchar",
		"numeric"
	],
CODE

И нужно указать явно, что все поля "Код" являются сенситивными:

"field": {                # must be anonymized without scanning
        "rules": [],
        "constants": [
        "_code"
        ]
CODE

Как определить самое часто упоминаемое слово в поле таблицы?

Для этого вы можете использовать следующий запрос:

SELECT word, COUNT(*) as frequency
FROM (
    SELECT unnest(string_to_array(lower(_Description::text), ' ')) as word
    FROM _Reference315
) as words
GROUP BY word
ORDER BY frequency DESC
LIMIT 1;
CODE

в котором нужно поменять 2 поля:

  • _Description - подставить имя вашего поля
  • _Reference315 - подставить имя вашего поля

Это поможет вам определить слово для мета-словаря, чтобы данное поле точно было анонимизировано.

Как анонимизировать конкретный справочник?

Если вам нужно анонимизировать в базе только один справочник, то его лучше прописать в разделе:

"include_rules": [{
            "schema": "public",
            "table": "_Reference315"
        }]
CODE

В этом случае поиск сенситивных полей будет осуществляться только в этом справочнике.

А если нужно сделать, чтобы анонимизировало конкретные поля в конкретном справочнике, то смотрим ответ на вопрос "Как определить самое часто упоминаемое слово в поле таблицы?".

Можно ли анонимизировать лишь часть базы?

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

Как добавить шаблон регулярного выражения и проверить его?

Для этого вам нужно добавить шаблон вашего регулярного выражения в раздел "data_regex". Проверить корректность его работы можно следующим запросом:

SELECT Fld69944
FROM Reference787_VT69940
WHERE CAST(_Fld69944 AS TEXT) ~ '^(7?\d{10})$';
CODE

где

  • Fld69944 - проверяемое поле
  • Reference787_VT69940 - проверяемая таблица
  • '^(7?\d{10})$' - проверяемое регулярное выражение

Что делать если после анонимизации базы возникает ошибка при открытии какого-то объекта?

Скорее всего было анонимизировано поле, на значения которого в коде 1С завязана логика. Нужно явно исключить такие поля из мета-словаря или уточнить правила анонимизации.

Какие коммерческие условия использования данного инструмента?

Консольная версия является опенсурсной и доступна всем желающим - https://github.com/TantorLabs/pg_anon

Версия с GUI доступна только в платформе Tantor. Подробнее о ней можно узнать здесь.