В современном информационном мире, информация играет значительную роль в жизни человека, общества и государства. Рост размера накапливаемых и обрабатываемых данных подымает вопросы об их хранении и обеспечении конфиденциальности. Уже существует немало технических решений и предложений для решения подобных задач. Среди них конечно же есть и системы управления базами данных (СУБД) которые поддерживают шифрование хранимых данных. Вот об одном из таких решений и пойдёт речь.

В апреле 2016 года вышла новая версия СУБД Firebird под номером 3. Из нововведений, среди прочего, появилось и немало механизмов защиты хранимых и передаваемых данных. Там есть и защита канала передачи данных, есть управление пользователями, а также есть шифрование самой БД, которое реализовано как прозрачное шифрование на уровне страниц данных. Реализуется это всё с помощью написания специальных расширений для Firebird. Можно конечно и самому разобраться и написать эти расширения, но почему бы не взять существующие. Тем более, что для написания, как минимум, нужно понимать в криптографии, иметь на вооружении знания какого-нибудь криптографического пакета и разобраться с новым С++ Firebird API.

Можно воспользоваться существующим готовым решением, скачав по этой ссылке для платформы Windows x64. В бесплатном ознакомительном варианте оно ограничено используемым алгоритмом шифрования — поддерживается Triple DES (3DES). Но для защиты своей БД этого вполне достаточно. В состав пакета входит 4 библиотеки и файлы с описанием интерфейсов.
Наименование файла Описание
CiKeyHolder.dll расширение Firebird – хранитель секретного ключа
CiDbCrypt.dll расширение Firebird – шифрования данных
CiFbEnc_x86.dll модуль активации ключа шифрования для платформы 32 bit.
CiFbEnc_x86-64.dll модуль активации ключа шифрования для платформы 64 bit.
ICiFbEncActivator.h описание интерфейса модуля для С++
CI.ICiFbEncActivator.pas описание интерфейса модуля для Delphi
Как пример, можно написать приложение, которое использует некий справочник (условно с конфиденциальными данными), который как обновляемый доступен по ссылке в глобальной сети. Его может скачать кто угодно, но при этом расшифровать и работать с ним сможет только наше приложение.

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

В качестве платформы возьмём ОС Windows 10 x64, а в качестве инструментов реализации задачи будем использовать IDE Embarcadero RAD Studio (на данный момент была доступна версия 10.2 Tokyo — 30-дневная пробная), поскольку она содержит удобные компоненты для работы с БД Firebird и применяется часто для разработки прикладных решений (пробную версию можно скачать по ссылке) и СУБД Firebird 3.0.2 x64, которую можно скачать по ссылке https://www.firebirdsql.org/en/firebird-3-0-2 для своей платформы. Так как нам в режиме разработки не нужен постоянно выполняемый сервер, да и перезапуск его может понадобиться неоднократно, то во время инсталляции выбираем способ запуска сервера как приложения.

Установка Firebird

Пароль для SYSDBA делаем по старинке masterkey и снимаем галочку отвечающую за запуск сервера после установки – пока не нужно.

После того, как был проинсталлирован пакет Firebird, необходимо произвести его настройку. Если же планируется использование Firebird в Embedded режиме, то такие же настройки нужно будет проделать в директории, где будет находиться используемая библиотека клиент fbclient.dll. Базовые файлы настроек находятся в корневой директории установленного Firebird — %ProgramW6432%\Firebird\Firebird_3_0.

Пропишем псевдоним (алиас) БД в файле databases.conf, добавив строку с указанием полного пути к будущей БД:

TESTDB = C:\TESTAPP\DB\TESTDB.FDB

Для того, чтобы Firebird знал, где ему искать ключи, необходимо определить параметр KeyHolderPlugin. Его можно указать или конкретно для выше описанного псевдонима или в файле firebird.conf. Добавляем или изменяем параметр конфигурации файла firebird.conf, определяя плагин хранитель секретного ключа:

KeyHolderPlugin = CiKeyHolder

Где искать плагины Firebird узнаёт также из конфигурационных файлов. Изменим файл plugins.conf, добавив следующие строки:

Plugin = CiKeyHolder {
	# Прописываем путь к плагину хранителю
 	Module = $(dir_plugins)/CiKeyHolder
}

Plugin = CiDbCrypt {
	# Прописываем путь к плагину шифрования
 	Module = $(dir_plugins)/CiDbCrypt
}

Директория под псевдонимом $(dir_plugins) подразумевает директорию plugins в корневом каталоге Firebird. Копируем туда ранее скачанные плагины CiKeyHolder.dll и CiDbCrypt.dll.

Поскольку мы предполагаем, что БД справочник у нас уже есть, создадим её средствами пакета Firebird, а именно, используя приложение isql. Жмём Win + R и выполняем следующую команду:

%ProgramW6432%\Firebird\Firebird_3_0\isql -q -user SYSDBA -password masterkey

Или же как-то по-другому запускаем isql, например, используя программу Far.

Следующие команды создадут БД TESTDB, псевдоним которой ранее был прописан в настройках и выведут информацию о созданной БД:

SQL> create database 'TESTDB';
SQL> create table t_1 (i1 int, s1 varchar(15), s2 varchar(15));
SQL> insert into t_1 values (1,'value 1-1','value 1-2');
SQL> insert into t_1 values (2,'value 2-1','value 2-2');
SQL> commit;
SQL> select * from t_1;

          I1 S1              S2
============ =============== ===============
           1 value 1-1       value 1-2
           2 value 2-1       value 2-2

SQL> show db;
Database: TESTDB
        Owner: SYSDBA
PAGE_SIZE 8192
Number of DB pages allocated = 196
Number of DB pages used = 184
Number of DB pages free = 12
Sweep interval = 20000
Forced Writes are ON
Transaction - oldest = 4
Transaction - oldest active = 5
Transaction - oldest snapshot = 5
Transaction - Next = 9
ODS = 12.0
Database not encrypted
Default Character set: NONE
SQL> quit;

Как видно из полученной информации по команде show db;, созданная БД в данный момент не зашифрована — Database not encrypted.

Теперь всё настроено и можно приступить к написанию приложения.

Создаём новый проект VCL Forms Application в IDE Embarcadero RAD Studio. Сохраняем проект, например, как testapp в директории C:\TESTAPP.

Для начала мы можем просто связать наше приложение с БД, обеспечив подключение и отключение к ней. Наша тестовая БД имеет одну таблицу, данные которой можно вывести при подключении. Из визуального оформления достаточно пары кнопок, таблица и навигатор с управлением данными. Для доступа к БД Firebird воспользуемся компонентами FireDAC. Далее представлена таблица с компонентами и их настраиваемыми параметрами.
Компоненты, помещаемые на форму тестового приложения
Класс компонента Наименование компонента Параметр Значение параметра
TFDPhysFBDriverLink FDPhysFBDriverLink1 - -
TFDConnection FDConnection1 DriverName FB
LoginPrompt False
Params\Database TESTDB
Params\UserName SYSDBA
Params\Password masterkey
Params\Protocol ipTCPIP
Params\Server localhost
TFDGUIxWaitCursor FDGUIxWaitCursor1 - -
TFDTransaction FDTransaction1 - -
TFDTable FDTable1 TableName T_1
TDataSource DataSource1 DataSet FDTable1
TDBGrid DBGrid1 DataSource DataSource1
TDBNavigator DBNavigator1 DataSource DataSource1
TButton Button1 Caption Connect
TButton Button2 Caption Disconnect
В итоге форма может выглядеть приблизительно так.

Форма в дизайнере среды разработки

Пропишем обработку нажатия на кнопку “Connect”:

FDConnection1->Connected = true;
FDTable1->Active = true;

И для “Disconnect”:

FDTable1->Active = false;
FDConnection1->Connected = false;

В случае использования Delphi всё аналогично:

FDConnection1.Connected := True;
FDTable1.Active := True;

. . . 

FDConnection1.Connected := False;
FDTable1.Active := False;

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

Win + X > Командная строка (администратор)

И выполнить команду:

“%ProgramW6432%\Firebird\Firebird_3_0\firebird” –a

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

Теперь можно собрать и запустить приложение для проверки возможности подключения к БД. После подключения к БД в таблице отобразятся данные.

Успешное подключение к БД

Настало время подключить модуль активации ключа и управления шифрованием к нашему приложению. Для этого копируем файлы CiFbEnc_x86.dll и ICiFbEncActivator.h, CI.ICiFbEncActivator.pas в директорию C:\TESTAPP. Стоит обратить внимание, что поскольку по умолчанию в среде разработки создаётся проект под 32-bit Windows и триал версия среды разработки не позволяет изменить целевую платформу, приложению необходим именно файл-модуль CiFbEnc_x86.dll.

Подключим заголовочный файл с описанием интерфейса модуля активации к файлу приложения:

#include "ICiFbEncActivator.h"

Или файл Ci.ICiFbEncActivator.pas в случае Delphi.

uses
  . . . , CI.ICiFbEncActivator;

Добавим на форму ещё три кнопки: “Encrypt”, “Decrypt” и “Get State”.

Дополнительные кнопки для запуска операций модуля

Для начала рассмотрим обработчик кнопки “Get State”. Что следует из названия, это даст нам возможность получить текущее состояние БД.

Самое главное, — это получить доступ к интерфейсу модуля активации. Его можно получать как для каждой операции, так и глобально или агрегируя в некий объект. Чтобы не городить классы-обертки, будем предполагать, что получаем объект модуля активации при каждой операции. Ниже представлен один из возможных способов сделать это.

// C++ Builder

// Загружаем модуль
std::unique_ptr<HINSTANCE__, decltype(&::FreeLibrary)> mHandle(
	::LoadLibraryEx(L"C:\\TESTAPP\\CiFbEnc_x86.dll",0,LOAD_WITH_ALTERED_SEARCH_PATH),
	&::FreeLibrary);

if (!mHandle)
{
	MessageBox(
		NULL,
		L"Модуль CiFbEnc_x86.dll не найден или рядом с ним нет fbclient.dll",
		L"Загрузка модуля",
		MB_OK|MB_ICONERROR);

	return;
}

// Получаем активатор
typedef CALL_CONV int(__stdcall *CREATEFUNCPTR)(CI::ICiFbEncActivator**);
CREATEFUNCPTR GetActivator = 
(CREATEFUNCPTR)::GetProcAddress(mHandle.get(), "createCiFBEncActivator");

if (!GetActivator)
{
	MessageBox(
		NULL,
		L"Не удалось получить из модуля CiFbEnc_x86.dll процедуру" 
              "createCiFBEncActivator"
		 " - попробуйте посмотреть, что пишет tdump.",
		L"Загрузка модуля",
		MB_OK|MB_ICONERROR);

	return;
}

CI::ICiFbEncActivator* pActivator = NULL;
GetActivator(&pActivator);
if (!pActivator) { ShowMessage("ERROR GetActivator!"); return; }

// . . .
//
// обращения к объекту модуля активации
//
// . . .

// Уничтожаем экземпляр активатора
pActivator->Destroy();
pActivator = NULL;

// Delphi

var
pActivator : ICiFbEncActivator;
res : Integer;
CreateActivator: TActivatorFunction;
mHandle : HINST;
. . .
begin
// Загружаем модуль
mHandle := LoadLibraryEx(
PChar('C:\TESTAPP\CiFbEnc_x86.dll'), 0, LOAD_WITH_ALTERED_SEARCH_PATH);

if mHandle = 0 then
begin
     MessageBox(
        Application.Handle,
        'Модуль CiFbEnc_x86.dll не найден или рядом с ним нет fbclient.dll',
        'Загрузка модуля',
        MB_OK OR MB_ICONERROR);
      Exit;
end;

// Получаем активатор
CreateActivator := GetProcAddress(mHandle, 'createCiFBEncActivator');
if not Assigned(CreateActivator) then
begin
     MessageBox(
        Application.Handle,
        'Не удалось получить из модуля CiFbEnc_x86.dll процедуру createCiFBEncActivator'
        +
        ' - попробуйте посмотреть, что пишет tdump.',
        'Загрузка модуля',
        MB_OK OR MB_ICONERROR);

      Exit;
end;

pActivator := nil;
res := CreateActivator(pActivator);
if not Assigned(pActivator) then begin ShowMessage('ERROR CreateActivator!'); Exit; end;

// . . .
//
// обращения к объекту модуля активации
//
// . . .
 

// Уничтожаем экземпляр активатора
pActivator.Destroy;
pActivator := nil;

FreeLibrary(mHandle);
end;

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

Теперь можно перейти и к получению состояния БД.

// C++ Builder

. . .

// Устанавливаем параметры подключения к БД
pActivator->SetDBAccess("localhost:TESTDB", "SYSDBA", "masterkey");

// Посмотрим, что нам расскажет о БД сервис Firebird
char stat_buf[1024] = { 0 };
size_t bufsize = sizeof(stat_buf);

int res = pActivator->GetStateSVC(stat_buf, bufsize);
String sStatMsg = (String)stat_buf;
if (Err_OK == res)
{
	MessageBox(NULL, sStatMsg.c_str(), L"Статус БД", MB_OK|MB_ICONINFORMATION);
}
else
{
	String sErrMsg = L"ERROR GetStateSVC: " + sStatMsg;
	MessageBox(
		NULL,
		sErrMsg.c_str(),
		L"Статус БД",
		MB_OK|MB_ICONERROR);

}

. . .

// Delphi

var
. . .
stat_buf : array[0..1023] of AnsiChar;
bufsize : NativeUInt;

. . .

// Устанавливаем параметры подключения к БД
res := pActivator.SetDBAccess('localhost:TESTDB', 'SYSDBA', 'masterkey');

// Посмотрим, что нам расскажет о БД сервис Firebird
bufsize := SizeOf(stat_buf);
ZeroMemory(@stat_buf, bufsize);
res := pActivator.GetStateSVC(stat_buf, bufsize);

if Err_OK = res then
begin
       MessageBox(Application.Handle, PChar(String(stat_buf)),
       'Статус БД', MB_OK OR MB_ICONINFORMATION);
end
else
begin
       MessageBox(Application.Handle, PChar(String(stat_buf)),
       'Ошибка', MB_OK OR MB_ICONERROR);
end;

. . .

В случае, если удалось подключиться к БД и получить информацию, мы увидим сообщение с детальной информацией о текущем состоянии.

Успешное получение текущего статуса БД

Как видно, БД всё ещё не зашифрована. Пора это исправить. Но для шифрования необходим ключ. И мы его определим как глобальную константу в рамках примера.

// C++ Builder

// От куда-то у нас есть ключ 192 бита
const uint8_t key[24] =
{
	0x06,0xDE,0x81,0xA1,0x30,0x55,0x1A,0xC9,
	0x9C,0xA3,0x42,0xA9,0xB6,0x0F,0x54,0xF0,
	0xB6,0xF9,0x70,0x18,0x85,0x04,0x83,0xBF
};

// Delphi

const
key : array [0..23] of Byte =
(
  	$06,$DE,$81,$A1,$30,$55,$1A,$C9,
	$9C,$A3,$42,$A9,$B6,$0F,$54,$F0,
	$B6,$F9,$70,$18,$85,$04,$83,$BF
);

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

// C++ Builder

. . .

// Устанавливаем параметры подключения к БД
pActivator->SetDBAccess("localhost:TESTDB", "SYSDBA", "masterkey");

// Устанавливаем ключ активатору
int res = pActivator->SetKey(&key, sizeof(key));
if (Err_OK != res) { ShowMessage("ERROR SetKey!"); pActivator->Destroy(); return; }

// Активируем доступ к ключу
res = pActivator->Activate();
if (Err_OK != res)
{
// Обрабатываем ошибку
	char errmsg[512] = {0};
	size_t esize = sizeof(errmsg);
	pActivator->GetFBStat(errmsg, esize);
	String sErrMsg = "ERROR Activate: " + String(errmsg);
	MessageBox(
		NULL,
		sErrMsg.w_str(),
		L"Активация доступа к ключу",
		MB_OK|MB_ICONERROR);

	pActivator->Destroy();
	return;
}

. . .

// Delphi

. . .

// Устанавливаем параметры подключения к БД
res := pActivator.SetDBAccess('localhost:TESTDB', 'SYSDBA', 'masterkey');

// Устанавливаем ключ активатору
res := pActivator.SetKey(@key, Length(key));
if Err_OK <> res then begin ShowMessage('ERROR SetKey!'); pActivator.Destroy; Exit; end;

// Активируем доступ к ключу
res := pActivator.Activate;
if Err_OK <> res then
begin
bufsize := SizeOf(errmsg);
    	ZeroMemory(@errmsg, bufsize);
	pActivator.GetFBStat(errmsg, bufsize);
	MessageBox(
		Application.Handle,
		PChar('ERROR Activate: ' + String(errmsg)),
		'Активация доступа к ключу',
		MB_OK OR MB_ICONERROR);

	pActivator.Destroy;
	Exit;
end;

. . .

Как видно, происходит передача ключа активатору и вызов функции активации. При этом модуль регистрирует функцию обратного вызова для доступа к ключу средствами расширения хранителя ключа. И это не зависит от режима работы Firebird. А безопасность передачи ключа гарантируется шифрованием на сессионных ключах. В вышеизложенном коде после вызова функции активации показан пример обработки возникновения возможной ошибки на уровне Firebird библиотеки. Подобным образом можно считывать текущий статус выполненной задачи для любой из операций с обращением к функционалу Firebird.

Теперь можем написать обработчики кнопок “Encrypt” и “Decrypt”. Если опустить проверку статуса выполненной задачи и предварительную активацию ключа, то достаточно только добавить вызов у объекта активации одноимённой функции:

// C++ Builder

// зашифровать
res = pActivator->Encrypt();

. . .

// расшифровать
res = pActivator->Decrypt();

// Delphi

// зашифровать
res := pActivator.Encrypt;

. . .

// расшифровать
res := pActivator.Decrypt;

В итоге мы получим идентичные обработчики за исключением одного вызова.

Теперь мы можем зашифровать БД. А вызов её статуса расскажет нам о том, что она уже зашифрована.

Статус зашифрованной БД

После этого, попытка выполнить подключение к БД не меняя обработчик кнопки “Connect” приведёт к ошибке.

Попытка подключения к зашифрованной БД без активации ключа

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

Для C++ Builder
// C++ Builder

void __fastcall TForm1::Button1Click(TObject *Sender)
{
// Загружаем модуль
 	std::unique_ptr<HINSTANCE__, decltype(&::FreeLibrary)> mHandle(
		::LoadLibraryEx(L"C:\\TESTAPP\\CiFbEnc_x86.dll", 0,
LOAD_WITH_ALTERED_SEARCH_PATH),
		&::FreeLibrary);

	if (!mHandle)
	{
		MessageBox(
			NULL,
			L"Модуль CiFbEnc_x86.dll не найден или рядом с ним нет fbclient.dll",
			L"Загрузка модуля",
			MB_OK|MB_ICONERROR);
		return;
	}

	// Получаем активатор
	typedef CALL_CONV int(__stdcall *CREATEFUNCPTR)(CI::ICiFbEncActivator**);
	CREATEFUNCPTR GetActivator = 
(CREATEFUNCPTR)::GetProcAddress(mHandle.get(), "createCiFBEncActivator");

	if (!GetActivator)
	{
		MessageBox(
			NULL,
			L"Не удалось получить из модуля CiFbEnc_x86.dll"
" процедуру createCiFBEncActivator"
			 " - попробуйте посмотреть, что пишет tdump.",
			L"Загрузка модуля",
			MB_OK|MB_ICONERROR);
		return;
	}

	CI::ICiFbEncActivator* pActivator = NULL;
	GetActivator(&pActivator);
	if (!pActivator) { ShowMessage("ERROR GetActivator!"); return; }

	// Устанавливаем параметры подключения к БД
	pActivator->SetDBAccess("localhost:TESTDB", "SYSDBA", "masterkey");

	// Устанавливаем ключ активатору
	int res = pActivator->SetKey(&key, sizeof(key));
	if (Err_OK != res) { ShowMessage("ERROR SetKey!"); pActivator->Destroy();return; }

	// Активируем доступ к ключу
	res = pActivator->Activate();
	if (Err_OK != res)
	{
		// Обрабатываем ошибку
		char errmsg[512] = {0};
		size_t esize = sizeof(errmsg);
		pActivator->GetFBStat(errmsg, esize);
		String sErrMsg = "ERROR Activate: " + String(errmsg);
		MessageBox(
			NULL,
			sErrMsg.w_str(),
			L"Активация доступа к ключу",
			MB_OK|MB_ICONERROR);
		pActivator->Destroy();
		return;
	}

	// Подключаемся к БД
	try
	{
		FDConnection1->Connected = true;
		FDTable1->Active = true;
	}
	catch(EFDDBEngineException &e)
	{
		String sErrMsg = L"Не удалось подключиться к БД. " + e.Message;
        	MessageBox(
			NULL,
			sErrMsg.w_str(),
			L"Подключение к БД",
			MB_OK|MB_ICONERROR);
	}

	// Уничтожаем экземпляр активатора
	pActivator->Destroy();
	pActivator = NULL;
}


Для Delphi
// Delphi

procedure TForm2.Button1Click(Sender: TObject);
var
pActivator : ICiFbEncActivator;
res : Integer;
CreateActivator: TActivatorFunction;
mHandle : HINST;
errmsg : array[0..511] of AnsiChar;
bufsize : NativeUInt;
begin
  // Загружаем модуль
	mHandle := LoadLibraryEx(PChar('C:\TESTAPP\CiFbEnc_x86.dll'), 0,
 LOAD_WITH_ALTERED_SEARCH_PATH);

  if mHandle = 0 then
  begin
     MessageBox(
        Application.Handle,
        'Модуль CiFbEnc_x86.dll не найден или рядом с ним нет fbclient.dll',
        'Загрузка модуля',
        MB_OK OR MB_ICONERROR);
      Exit;
  end;

  // Получаем активатор
  CreateActivator := GetProcAddress(mHandle, 'createCiFBEncActivator');
  if not Assigned(CreateActivator) then
  begin
     MessageBox(
        Application.Handle,
        'Не удалось получить из модуля CiFbEnc_x86.dll процедуру createCiFBEncActivator'
 +
        ' - попробуйте посмотреть, что пишет tdump.',
        'Загрузка модуля',
        MB_OK OR MB_ICONERROR);
		  Exit;
  end;
  pActivator := nil;
  res := CreateActivator(pActivator);
  if not Assigned(pActivator) then begin ShowMessage('ERROR CreateActivator!');Exit; end;

  // Устанавливаем параметры подключения к БД
  res := pActivator.SetDBAccess('localhost:TESTDB', 'SYSDBA', 'masterkey');

  // Устанавливаем ключ активатору
  res := pActivator.SetKey(@key, Length(key));
  if Err_OK <> res then begin ShowMessage('ERROR SetKey!'); pActivator.Destroy;Exit; end;

  // Активируем доступ к ключу
  res := pActivator.Activate;
  if Err_OK <> res then
  begin
	bufsize := SizeOf(errmsg);
    	ZeroMemory(@errmsg, bufsize);
	pActivator.GetFBStat(errmsg, bufsize);
	MessageBox(
		Application.Handle,
		PChar('ERROR Activate: ' + String(errmsg)),
		'Активация доступа к ключу',
		MB_OK OR MB_ICONERROR);

	pActivator.Destroy;
	Exit;
  end;

  // Подключаемся к БД
  try
    	FDConnection1.Connected := True;
FDTable1.Active := True;
  except
    	on E: EFDDBEngineException do begin
       MessageBox(
        Application.Handle,
        PChar('Не удалось подключиться к БД. ' + String(E.Message)),
        'Подключение к БД',
        MB_OK OR MB_ICONERROR);
    end;
  end;

  // Уничтожаем экземпляр активатора
  pActivator.Destroy;
  pActivator := nil;

  FreeLibrary(mHandle);
end;


Вот так вот достаточно просто можно работать с зашифрованной БД.

Полностью проект можно взять тут.

Демо-пакет — тут.
Поделиться с друзьями
-->

Комментарии (26)


  1. sekoles
    16.06.2017 08:28

    Извините, но создавать БД просто «create database 'TESTDB';» без указания параметров аукнется.


    1. cipher-bis
      16.06.2017 09:46

      Вопрос создания БД с различными параметрами выходит за рамки этой статьи, поэтому указан самый простой, тестовый пример. Основной упор сделан на демонстрацию возможностей использования шифрования БД.


  1. redmanmale
    16.06.2017 13:40
    -3

    Delphi, C++ Builder, Firebird… извиняюсь, конечно, но как будто статья из 2004.


    1. AlexeyKovyazin
      16.06.2017 14:06
      +2

      У молодежи всегда складывается впечатление, что технологии, которые запустились раньше их трудовой карьеры — дикое устаревшее старье :) Про то, что цикл жизни ERP и прочего энтерпрайза составляет лет 20, мало кто знает.


  1. qw1
    18.06.2017 14:52

    Любого студента ИБ на первом курсе учат:
    1. Опишите модель угроз!

    Без этого — зачем всё вышеизложенное?
    От кого защищаемся? Если от легального пользователя приложения, что ему мешает в WinDBG поставить breakpoint на ф-цию SetKey и немедленно получить ключ?

    Пока не вижу пользы, одно только прожигание электроэнергии клиента и сервера на бесполезное шифрование.


    1. cipher-bis
      20.06.2017 17:51

      Что касаемо модели угроз, то можно просто сослаться на ряд стандартов безопасности, в которых явно идет речь о защите данных в БД (HIPAA, ISO/IEC 27001, FERPA, PCI-DSS).

      Вот ссылка на презентацию, где есть об этом информация.

      Но статья скорее ориентирована просто на описание пошагового применения и предполагает, что тот кому это применение применимо в его модели, то вот так вот это можно сделать.

      По поводу: «От кого защищаемся?» — в статье есть слова: "… использует некий справочник (условно с конфиденциальными данными), который как обновляемый доступен по ссылке в глобальной сети. Его может СКАЧАТЬ КТО УГОДНО, но при этом расшифровать и работать с ним сможет только наше приложение." — вроде понятно, что угрозой является свободный доступ к файлу в глобальной сети. Защищаем данные от свободного доступа к ним, и, в этом конкретном описании, никак не от легального пользователя приложения.

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


      1. qw1
        20.06.2017 19:53

        То есть сценарий, когда порт СУБД выставлен наружу в сеть.
        Но в этом случае есть защита логином/паролем + шифрование соединения, появившееся в FB 3.

        Пока это выглядит как слой дополнительный защиты. С одной стороны, перестраховка от неизвестных уязвимостей FB. С другой стороны, такой подход в безопасности как «вроде оно уже защищено, но давайте на всякий случай добавим ещё один слой» — не приветствуется.


        1. cipher-bis
          20.06.2017 20:04

          Да нет, тут не имелось ввиду использования СУБД с доступом из глобальной сети. Просто показано, что файл с БД можно не опасаясь хранить где-угодно.


      1. qw1
        20.06.2017 20:04

        Или сценарий, когда по HTTP выставлен файл БД (очень странный сценарий для БД)?
        В этом случае, достаточно положить в 7z-архив с паролем, и не городить столько сложностей.


        1. t-jet
          20.06.2017 20:17

          Мне стала интересна ваша полемика. Хочу добавить немного слов в защиту автора. Современные СУБД Enterprise уровня (Oracle DB Ent., Oracle MySQL Ent., Microsoft SQLServer Ent., даже MongoDB) обладают такой функцией, как TDE (transparent data encryption), которая подразумевает прозрачное шифроврование табличного пространства. Эти все навороты направлены на удовлетворене неких требований HIPAA, PCI-DSS. Думаю, что если попросить автора подготовить отдельную статью по поводу разбора угроз, которые покрываются данным решением, будет корректнее.
          «Рубится» относительно корретности предложенного примера, соглашусь, что он не сильно удачный :-)
          Могу предложить более удачный:

          • вы разработчик грфического редактора, у которого есть некие фильтры, которые красиво преобразуют вашу картинку
          • параметры этих фильтров храняться в шифрованной БД (она может быть, как локальной, так и развернутой на выделенном сервере)
          • ключ шифрования, как-то привязан к вашему коду активации графического редактора


          Теперь, что получается… что только авторизованный пользователь приложения пользуется этим приложением и конкуренты не смогут «легко» украсть эти параметры.


          1. qw1
            20.06.2017 21:41

            Теперь, что получается… что только авторизованный пользователь приложения пользуется этим приложением и конкуренты не смогут «легко» украсть эти параметры.
            Купить одну копию приложения никак нельзя? ))


            1. t-jet
              21.06.2017 00:02

              Купить одну копию приложения никак нельзя? ))

              Вопрос в том, что купить как раз и можно! Вот только значения коэфциентов, хранящиеся в БД получить нельзя, т.к. пользователь легитимно владеющий Графическим редактором не может получить доступ к содержимому БД, т.е. к коэфициентам.


              1. qw1
                21.06.2017 20:04

                То есть алгоритм запускается на машине пользователя, «секретные коэффициенты» находятся в памяти, но получить их нельзя, потому что недостаточно навыков реверс-инженеринга, так?

                Тут можно сказать, что и алгоритм заполучить нельзя, потому что исходников программы нет ))

                И из нешифрованной БД нельзя получить коэффициенты, потому что в таблицах одни числа и никаких пояснений ))


                1. t-jet
                  21.06.2017 20:36

                  Приятно общаться с человеком, который готов "сражаться" за свое мнение :-)
                  Вопрос reverse-engineering действительно актуален, и здесь даже очень серьезные компании с огромными финансовыми и человеческими ресурсами ничего не могут сделать, т.к. сложность защиты от reverse-engineering достаточно высока.
                  Идея которая эксплуатируется у автора полностью аналогична решению TBD от Oracle DB, Microsoft SQLServer, MongoDB, Oracle MySQL Enterprise и позволяет лишь существенно усложнить задачу reverse-engineering, не более того.
                  Насколько мне не изменяет память, то сложность этой задачи (защиты от reverse-engineering) определяется механизмами защиты в ОС (Windows или Linux).


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


                  Предлагаю остановиться на том, что та же HIPAA (я не поленился нашел о чем говорит автор) https://en.wikipedia.org/wiki/Health_Insurance_Portability_and_Accountability_Act
                  там есть в разделе: Technical Safeguards


                  Information systems housing PHI must be protected from intrusion. When information flows over open networks, some form of encryption must be utilized. If closed systems/networks are utilized, existing access controls are considered sufficient and encryption is optional.

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


                  1. qw1
                    21.06.2017 22:37

                    Предлагаю остановиться на том, что та же HIPAA
                    When information flows over open networks, some form of encryption must be utilized. If closed systems/networks are utilized, existing access controls are considered sufficient and encryption is optional.
                    Это значит, что предложенная в статье разработка не нужна для соответствия процитированным требованиям. Встроенного в FB 3.0 Wire Encryption уже достаточно.


        1. cipher-bis
          20.06.2017 20:20

          Ключевым является безопасное хранение файла БД с доступом к данным только из определённого приложения. 7z архив же нужно будет распаковать/расшифровать и данные (то ли локально, то ли на сервере под властью сисадминов) будут храниться опять же открыто.


          1. qw1
            20.06.2017 21:39

            Опять же, от кого защищаемся? От сисадминов?
            Или от злоумышленника, имеющему доступ к серверу? Если он имеет доступ администратора, он перехватит ключи из памяти. А если не имеет такого доступа, достаточно настроить запрет чтения файла.


            1. cipher-bis
              21.06.2017 12:01

              Опять же, не забываем, что мы сейчас обсуждаем демонстрационный пример. И тут всё максимально просто. Вокруг этого можно много чего городить.

              И мы не защищаемся, а защищаем хранимые данные как личное имущество. А защищать своё имущество можно по-разному, — можно просто дома хранить (но при этом мы дверь таки запираем), а можно и в банке в надёжном сейфе. И везде злоумышленник может преуспеть. Всё сводится к отношению стоимости затрат к стоимости желаемого.

              Разбирать детали реализации слишком долго и можно написать об этом целую статью. Поэтому кратко по поводу перехвата на сервере ключа (в случае хранения БД на отдельном сервере):

              • клиентская часть на сервере недоступна, т.е. модуль активации принимающий ключ находится удалённо;
              • ключ передаётся в зашифрованном виде с помощью сессионных ключей (об этом в статье было);
              • ключ на серверном процессе извлекается именно во время процесса шифрования и это происходит в рамках динамического создания объекта для каждой сессии;
              • ключ даже в алгоритм шифрования передается в маскируемом виде и даже получение в маскируемом виде ключа, без наших ноу-хау ничего не даст;
              • запрет на чтение, это обычный саботаж и для этого используются административные способы, — например доступ к серверу для администрирования не единолично, а комиссионное.


              1. qw1
                21.06.2017 20:21

                И мы не защищаемся, а защищаем хранимые данные как личное имущество.
                Примерно так я и написал выше — ничего принципиально нового нет, добавляется ещё один уровень защиты, который добавляет нервотрёпки взломщику, но не закрывает какие-то векторы атаки, если они были открыты.

                • клиентская часть на сервере недоступна, т.е. модуль активации принимающий ключ находится удалённо;
                • ключ передаётся в зашифрованном виде с помощью сессионных ключей (об этом в статье было);
                • ключ на серверном процессе извлекается именно во время процесса шифрования и это происходит в рамках динамического создания объекта для каждой сессии;
                • ключ даже в алгоритм шифрования передается в маскируемом виде и даже получение в маскируемом виде ключа, без наших ноу-хау ничего не даст;
                Ой, сколько сложных наворотов, но хакер всегда идёт самым простым и прямым путём.

                Ясно, что криптомодуль взаимодействует с СУБД через публичные API, их и надо атаковать.

                Внедриться в любой процесс FB, в котором есть сессия авторизованного юзера со всеми ключами, остановить выполнение текущего запроса, запустить ф-цию FB, которая читает страницу с диска (на уровне уже после шифрования). Повторить для каждой страницы БД, записать результат в файл.

                Да ещё проще, если сделать бекап через gbak, gbk-файл будет незашифрованным.

                Вся инфраструктура сама всё сделает. Всё равно, что «взломать» truecrypt обычной командой copy с зашифрованного диска на незашифрованный.

                запрет на чтение, это обычный саботаж и для этого используются административные способы, — например доступ к серверу для администрирования не единолично, а комиссионное.
                Это интересно, но для Windows Server я таких решений не знаю.


                1. qw1
                  21.06.2017 20:31

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


                  1. cipher-bis
                    22.06.2017 12:52

                    Согласно, официальной документации, архитектура Firebird 3 поддерживает плагины. Сторонние разработчики, используя предоставленное API Firebird, могут реализовывать собственные плагины. В отличии от Firebird, разработанные плагины – third party и могут иметь закрытый исходный код.

                    Используя предоставленные возможности, можно разработать плагины, которые помогают защищать БД, например, используя шифрование. В этом случае шифруются данные в файле БД. Если этот файл каким-то образом попадает в руки злоумышленника – то данные в нем в зашифрованном виде.

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


                    1. qw1
                      22.06.2017 20:36

                      Вопрос в том, можно ли достичь той же степени безопасности только орг. мерами.


  1. t-jet
    19.06.2017 09:36

    Давно пользуюсь этой СУБД в своих проектах, пока не подводила… Замечу, что ядро разработчиков составляют ребята из России. Наверное многие будут говорить о наиболее популярных СУБД MySQL, PostgreSql и другие, но следует заметить, что для каждой задачи следует подбирать соответствующие инструменты…
    Я вот знаю, что компания RedSoft использует свой клон Firebird под названием RedDatabase. И используется в целом ряде федеральных агентств и служб. И все познается в сравнении.
    Предложу redmanmale попробовать Firebird 3.0, а вдруг понравится.


    1. ncix
      19.06.2017 15:09
      +1

      Много лет писал под FB, потом сменил работу, проекты, теперь в основном MSSQL, иногда MySQL. Ну что сказать, до сих пор вспоминаю IBExpert, невероятно удобный в сравнении с SSMS. Да и PSQL против T-SQL выигрывает просто на голову в логичности и возможностях языка.


      Но у FB (по крайней мере до 2.5, на котором я закончил) были серьезные проблемы с масштабированием. Нельзя было просто взять докинуть ресурсов на сервер и дальше ворочать многогигабайтную базу сложной структуры. Как с этим дела в 3.0?


      1. t-jet
        19.06.2017 17:50

        Признаюсь честно, что приходиться работать с БД под управлением СУБД Firebird 2.5.2 и СУБД Firebird 3.0.1 с размером от 1 ГБ и не более 10 ГБ. Сложность БД не очень, но содержит много BLOB (сертификаты, запрос на сертификаты, списки отозванных сертификатов и дельты списков отозванных сертификатов).
        Поведение предсказуемое на RAID 1+0 на SAS 6G. Для пущей уверенности используем SSD.
        ребята из IBExpert делают регулярную рассылку, где они тестируют конфигурации и показывает каким должен быть сервер для каких типов БД и с каким числом подключений.

        Я не хочу сказать, что эта СУБД — панацея. Просто есть можество задач, где он себя показывает отлично!

        Я вот почитал по СУБД Firebird 3.0.1, так у них действиетльно в архитектуре есть именно специализированные плагины для полного закрытия информации при работе с СУБД. Подобных решений я не нашел MySQL Community, PostgreSql… тем более, что нформацию, которую так нужно защищать не так и много…
        думаю, Firebird найдет и здесь свою нишу :-)


      1. t-jet
        20.06.2017 20:21

        Обратил внимание, что не ответил на ваш вопрос относительно масштабирования.
        Есть платная надстройка называется HQBird. Лично ей не пользовался, но отзывы достойные.