Моят личен и професионален живот

2018-11-15

Маскиране на данни и анонимизация в изданието за обществото на MySQL

Oracle наскоро въведе функционалност за маскиране на данни в MySQL, но тя е достъпна само в изданието за предприятия (виж MySQL Enterprise Data Masking and De-Identification в наръчника). Не съм клиент, но съм запознат с идеята както и със съхранени процедури, така че реших да се опитам да емулирам това което MySQL са изработили за тяхното издание за предприятия на базата.

Започнах с четене на наръчника, после прегледах форматите за номера на кредитна карта и социална осигурвока, за да изляза накрая с просто изпълнение изработено тази вечер. Изработих само функциите за маскиране с общо и специално предназначение и функциите за създаване на случайни данни (напр. е-поща, номер на карта за разплащания, номер на социална осигуровка и телефонен номер в САЩ). Имам допълнителна функция gen_rnd_string за създаване на низ от случайни символи с дадена дължина (само знаци от английската азбука по подразбиране). Направих няколко теста и резултатите изглеждат обещаващо, но трябва да пробвам повече и вероятно да създавам по-добри данни, които евентуално да минават проверка, защото приложение използващо случайно създадени данни може лесно да се счупи ако не може да ги провери. Остава ми да изработя функциите за създаване на случайни данни с използване на речници за които смятам, че мога да ползвам LOAD DATA INFILE и CREATE TEMPORARY TABLE за зараждане на речник.

Това е просто моята идея за това как може да бъде изработено маскиране на данни и анонимизация дори в изданието за обществото на MySQL и като съхранени процедури, защото реализацията на MySQL се основава на потребителски дефинирани функции (UDF). Чудя се защо са избрали този подход и отдолу опита ми да отгатна възможните отговори:
  • Видимост: Потребителски дефинираната функция има глобална видимост, така че може да бъде извикана без името на схемата отпред като всяка друга вградена функция. Моята реализация трябва да бъде заредена в схема и така всяка функция ще трябва да се вика с име на схемата отпред.
  • Подразбрани аргументи: MySQL все още не поддържа подразбрани аргументи за съхранени процедури, но това е възможно за потребителски дефинирани функции тъй като те могат да се написани само "на C или C++ (или друг език, който може да използва C конвенция на извикванията)". Функциите за маскиране с общо предназначение mask_inner и mask_outer приемат незадължителен аргумент за символа с който да се маскира. В реализация със съхранени процедури това може да стане само с потребителски дефинирана променлива. Ще изработя това по-късно.
  • Скриване на подробностите по изработката: Потребителски дефинираните функции скриват изработката на MySQL в двоичния файл на споделената библиотека. Изходния код на съхранените процедури може лесно да бъде разгледан от потребител с достатъчно права (напр. администратор). До колкото ми е известно MySQL не предлага инструмент за разбъркване на кода както Oracle (виж Obfuscating PL/SQL Source Code). Отделно от това наръчника не предоставя достатъчно подробности за възможните стойности от функциите за създаване на данни.
  • Сигурност: Това може да се отнася до начина по който сървъра управлява паметта за потребителски дефинираните функции и съхранените процедури, но това е твърде навътре в нещата.
  • Производителност: Потребителски дефинираните функции по предположение трябва да имат по-добра производителност в сравнение със съхранените процедури, защото първите са извиквания на функции в компилирана за съответната система съхранена библиотека, а вторите се интерпретират от сървъра при всяко извикване.
Нетърпелив съм да обсъдя това утре на Есенната конференция на БГПО 2018 където ще има представяне на точно същата тема. Ще обновя публикацията или ще напиша нова след събитието.

Обновяване 2018-11-16 19:01 EET: BGOUG конференцията потвърди представите ми за паричните, поради които маскирането на данни е изработено като UDF функции. Една от цитираните причини беше точно "производителност".

Обновяване 2018-11-18 19:51 EET: След като си поиграх още малко и опитах да направя речниковите функции днес, се сблъсках с две ограничения, за които не бях помислил преди това. Първото ограничение е невъзможността за използване на подготвени изрази във функция (т.е. от главата Prepared SQL Statement Syntax в наръчника "SQL syntax for prepared statements can be used within stored procedures, but not in stored functions or triggers"). Второто ограничение е невъзможността за използване на LOAD DATA INFILE като подготвен израз (виж секция SQL Syntax Allowed in Prepared Statements в наръчника). Първото ограничение означава, че речниковите функции могат да бъдат изработени само като процедури. Второто ограничение означава, че е невъзможно да се зареди речника от файл, така че изработката на функция gen_dictionary_load не може да бъде завършено - тя може да създаде временната таблица за речника, но не може да го зареди. Все пак направих процедурите и успях да създам случайни градове в САЩ. Също така проверих производителността с gen_rnd_pan функцията, тъй като тя прави както създаване на случаен низ от цифри, така и изчисление на числото на Лун. На моя MySQL 5.7 сървър работещ върху Intel Pentium G3420 @ 3.2 GHz с 8 GB DDR3 RAM пуснах SELECT BENCHMARK(count, gen_rnd_pan()) и резултата за различен брой повторения беше следния:
  • 1000 пъти - 0.28 секунди;
  • 10000 пъти - 3.13 секунди;
  • 100000 пъти - 31.41 секунди;
  • 1000000 пъти - 278.19 секунди.
Освен ако MySQL не премахне ограниченията за мен темата е приключена. Мога да се опитам да направя функциите за маскиране на данни като UDF както MySQL ако намеря време и ако имам истинска нужда.

Няма коментари: