"Статья для участия в конкурсе Тестирование Веб-Приложений на проникновение"
Предыстория
Безопасность данных всегда была, есть, и остается больной темой для всех людей и айтишников. Говорить по данной тематике можно бесконечно, иначе говоря от слов к делу. В недалёком 2014 году, когда учился на 5 курсе речь зашла о дипломе и практических навыках, полученных за всё обучение. Почему остановился на разработке, да ещё и связанной с биометрией, ответ прост – были наработки. Целью работы являлось – реализовать ПО для получения биометрических данных, иначе говоря – конечная задача свелась к получению картинки с специальными точками, по которым производится идентификация.
Что бы реализовать ПО выделил для себя основные моменты:
- минимизировать число необходимых процедур и функций, для увеличения быстродействия и удобства дальнейшего использования;
- вкратце описать модули разработчика, потому как лезть в дизассемблер очень затяжное дело, и скорее всего не получилось бы;
- разработать блок схему для общего представления о функционале;
- описать типовые решения применения.
После непродолжительных танцев с бубном и трассировкой Delphi – вуаля, сканы пальцев со спец точками корректно создавались. Реализация свелась к любимой всеми теории, где основными моментами стали:
Анализ биометрических данных
основываясь на законодательстве в области обработки ПДн, а именно Федеральном законе РФ "О персональных данных" (152-ФЗ), глава 2, статья 11, следует, что биометрические персональные данные – это:
- 1. Сведения, которые характеризуют физиологические и биологические особенности человека, на основании которых можно установить его личность (биометрические персональные данные) и которые используются оператором для установления личности субъекта персональных данных, могут обрабатываться только при наличии согласия в письменной форме субъекта персональных данных, за исключением случаев, предусмотренных частью 2 настоящей статьи.
- 2. Обработка биометрических персональных данных может осуществляться без согласия субъекта персональных данных в связи с реализацией международных договоров Российской Федерации о реадмиссии, в связи с осуществлением правосудия и исполнением судебных актов, а также в случаях, предусмотренных законодательством Российской Федерации об обороне, о безопасности, о противодействии терроризму, о транспортной безопасности, о противодействии коррупции, об оперативно - розыскной деятельности, о государственной службе, уголовно – исполнительным законодательством Российской Федерации, законодательством Российской Федерации о порядке выезда из Российской Федерации и въезда в Российскую Федерацию.
В качестве анализа, также можно привести экспертные оценки свойств биометрических характеристик человека:
В практическом применении метода биометрических характеристик главным параметром является универсальность и уникальность той или иной технологии.
Анализ алгоритмов получения биометрических данных и методов идентификации при помощи особых точек на отпечатках пальцев;
Отпечаток пальца состоит из хребтов и впадин, которые образуют папиллярный рисунок (рисунок 1). Отпечаток имеет два вида признаков: глобальные и локальные. Глобальные признаки описывают общее положение папиллярных линий.
Рисунок 1 - Отпечаток пальца с отмеченными папиллярными линиями
Локальные признаки отпечатков пальцев описывают расположение линий в окрестности минуций. Минуция - такая точка отпечатка пальца, где папиллярная линия обрывается или разделяется на две. Из этих двух типов могут быть составлены более сложные виды минуций (рисунок 2).
Рисунок 2 - Разные виды минуций
Особые точки на отпечатках пальца относятся к глобальным признакам и бывают двух видов: точка ядра и точка дельты. Ядро - точка отпечатка пальца, которую огибает максимальное количество папиллярных линий (рисунок 3).
Рисунок 3 - Точка ядра на отпечатке пальца
Дельта - точка отпечатка пальца, вокруг которой папиллярные линии расходятся в трех разных направлениях (рисунок 4).
Рисунок 4 - Точка дельты на отпечатке пальца
Локальные признаки, в отличие от глобальных, у каждого человека являются индивидуальными. Поэтому для идентификации пользуются методом распознавания по минуциям. Т.е. информация, полученная в результате съема отпечатка со сканнера, сравнивается с информацией в базе данных. Данный метод имеет ряд недостатков, таких как трудоемкость, чувствительность к локальному растяжению и плохая масштабируемость.
Также на положении особых точек основывается классификация отпечатков пальцев. Тип отпечатка пальца - еще один вид глобальных признаков. Отпечатки разделяются на три основных типа: спираль, петля и дуга (рис. 5).
Рисунок 5 - Отпечатки типа «Петля», «Спираль» и «Дуга»
Для определения особых точек на отпечатке пальца существует четыре основных подхода к определению особых точек: метод индекса Пуанкаре; метод комплексных фильтров 1 степени; метод комплексных фильтров 2 степени; метод индекса Пуанкаре с преобразованием Хафа.
Метод индекса Пуанкаре и метод комплексных фильтров 1 степени, реализованы евклидовым расстоянием между особыми точками, что является наиболее распространёнными методами. Углубляться в мат часть не вижу смысла, получится copy/past.
Адекватно описать функционал получения биометрических данных пользователя собственного ПО;
Для решения поставленных целей, будет использоваться часть комплекта разработчика, связанная с языком программирования Object Pascal: Nffv.pas; NffvUser.pas; UserList.pas; Util.pas
Рассмотрим модуль Nffv.pas, который является контейнером методов и классов для использования бесплатной версии SDK.
Вначале остановимся на типе данных для отображения статуса сканера (листинг 1):
Листинг 1.
Код:
type
TNffvStatus = ( nfesNone = 0, nfesTemplateCreated = 1,
{ временные данные сканера(подключение базы/инициализация драйвера). }
nfesNoScanner = 2, { наличие сканнера }
nfesScannerTimeout = 3, { сканер находится в ожидании}
nfesUserCanceled = 4, { отменение действий пользователем }
nfesQualityCheckFailed = 100 { наличие ошибок}
);
Листинг 2.
Код:
type
TNffv = class
private
public
Constructor Create(databaseName: string; password: string); overload;
{ Создание нового экземпляра обьекта TNffv(параметры базы данных, пароля и модулей сканера). }
Constructor Create(databaseName: string; password: string; scannerModules: string); overload;
{конструктор, инициализирующий сканер, от параметров //обьекта TNffv}
Destructor Destroy; override;
{деструктор объекта}
procedure RemoveUsers;
{очистка стандартной базы данных}
procedure RemoveUser(index: LongInt);
{удаление пользователя по индексу в базе}
function GetUserCount: LongInt;
{Получение номера пользователей контейнера Nffv}
function GetUserByIndex(index: LongInt): TNffvUser;
{возвращает индекс выбранного пользователя}
function GetUserById(id: LongInt): TNffvUser;
{возвращает идентификатор выбранного пользователя}
function GetUserIndexById(id: LongInt): LongInt;
{возвращает индекс параметром идентификатора}
procedure Cancel;
{Отменна сканирования отпечатков}
function Enroll(timeout: LongWord; var engineStatus: TNffvStatus): TNffvUser;
{Получение отпечатка }
function Verify(user: TNffvUser; timeout: LongWord; var engineStatus: TNffvStatus): LongInt;
{Сравнение пользователей}
function GetQualityThreshold: Byte;
{Порог качества изображения}
procedure SetQualityThreshold(threshold: Byte);
{Выставляем порог качества}
function GetMatchingThreshold: LongInt;
{Устанавливаем минимальное значение соответствия схожести}
procedure SetMatchingThreshold(threshold: LongInt);
{Минимальное значение схожести отпечатков}
end;
function GetAvailableScannerModules(): String;
{Обьявление модулей сканера}
function NffvGetInfo(): TNLibraryInfo;
{обьявление Nffv библиотеки}
function EngineStatusString(status: TNffvStatus): string;
{Строковое сообщение о TNffv статусе аппарата}
procedure NffvFreeMemory(point: PChar); stdcall; external dllName;
{высвобождаем память созданную в работе движка}
Листинг 3.
Код:
type
TNffvUser = class
private
_handle: Pointer;
public
Constructor Create(handle: Pointer);
{Создаём новый экземпляр TNffvUser}
property Handle: Pointer read _handle;
{метод хэндла}
function GetId(): LongInt;
{Получаем идентификатор пользователя}
function GetImage(): TBitmap;
{Получаем изображение отпечатка пальца}
Листинг 4.
Код:
type
TUser = class
{класс для работы с пользователями}
Public
UserName: string; {имя пользователя}
UserID: LongInt; {идентификатор пользователя}
constructor Create(const Name : String;
{конструктор класса}
const ID : Integer);
{идентификатор пользователя}
end;
Листинг 5.
Код:
TUserDatabase = class(TList)
{класс для работы со стандартной базой данных}
private
_databaseName: String;{привязка ini файла к базе данных}
public
procedure Save;
{процедура сохранения ланных пользователя}
function GetById(id: LongInt): TUser;
{получение идентификатора}
Constructor Create(databaseName: string);
{конструктор класса}
Destructor Destroy; override;
{деструктор класса}
end;
Листинг 6.
Код:
type
TNLibraryInfo = record
{запись о разработчиках SDK}
Title: array[0..63] of Char;{экспорт названия}
Product: array[0..63] of Char;{продукт}
Company: array[0..63] of Char;{компания}
Copyright: array[0..63] of Char;{авторское право}
VersionMajor: Integer;{главная версия}
VersionMinor: Integer;{порядковая версия}
VersionBuild: Integer;{версия сборки}
VersionRevision: Integer;{версия доработки}
DistributorId: Integer;{идентификатор дистрибьютора }
SerialNumber: Integer;{серийный номер}
end;
function Succeeded(Res: Integer): Boolean;
{функция вызываемая при успешном выполнении работы программы}
function Failed(Res: Integer): Boolean;
{функция вызываемая при выполнении //неудачного сканирования}
procedure RaiseError(msg: String; Err: Integer); overload;
{процедура исполняемая при возникновении ошибки}
procedure RaiseError(msg: String); overload;
{процедура исполняемая при возникновении ошибки}
function GetOS: String;
{функция получения рабочей платформы}
function MatchingThresholdToString(value: Integer): string;
{перевод значения минуции в строковый параметр}
function MatchingThresholdFromString(value: string): Integer;
{вызов параметра минуции из строки}
Функция Failed выдаёт ошибку, в случае если Res < 0
Листинг 7.
Код:
function Failed(Res: Integer): Boolean;
begin
Result := Res < 0;
end;
Листинг 8.
Код:
function GetAvailableScannerModules(): String;
var res: Integer;{}
str: PChar;
str2: String;
resultString: String;
begin
res := NffvGetAvailableScannerModulesA(str);
{в переменную res записываем строковый параметр срабатывания сканера}
str2 := string(str);
{переводим значение указателя в строковый параметр}
resultString := str2;
{в результирующую строку записываем строку с значением параметра}
if (Failed(res)) then
begin
RaiseError('невозможно получить доступ к модулям сканера.', res);
end;
{обработка ошибки}
Result := resultString;
NffvFreeMemory(str);
end;
Для того чтобы подключить сканнер Futronic Fs80, прописываем код(листинг 9):
Листинг 9.
Код:
Engine := TNffv.Create('fingers.db', '', 'Futronic');
Engine.RemoveUsers чистка стандартной базы данных приведена процедурой RemoveUsers(листинг 10):
Листинг 10.
Код:
procedure TNffv.RemoveUsers;
var res: Integer;
begin
res := NffvClearUsers();
{переменной присваиваем обнуление базы данных}
if (Failed(res)) then
begin
RaiseError('удаление пользователей невозможно.', res);
end;
{при возникновении ошибки выводится сообщение «удаление пользователей невозможно»}
end;
Листинг 11.
Код:
_user := Engine.Enroll(2000, _engineStatus);
Листинг 12.
Код:
function TNffv.Enroll(timeout: Cardinal; var engineStatus: TNffvStatus): TNffvUser;
var res: Integer;
engStatus: Integer;
handle: Pointer;
begin
res := NffvEnroll(timeout, engStatus, handle);
{значение переменной рассчитано из времени, значения статуса сканирования, и значения хэндла}
if (Failed(res)) then
begin
RaiseError(' добавление пользователя невозможно.', res);
end;
{при возникновении ошибки выводится сообщение «добавление пользователя невозможно»}
engineStatus := TNffvStatus(engStatus);
{значение статуса = числовому значению состояния сканирования}
if (engineStatus = nfesTemplateCreated) then
{если числовое значение статуса = значению созданного шаблона }
begin
Result := TNffvUser.Create(handle);
{создаём пользователя по его хэндлу}
end
else
Result := nil;
{иначе в результат записывается нулевое значение}
end;
Листинг 13.
Код:
function TNffvUser.GetImage: TBitmap;
var res: Integer;
nBitmap: TBitmap;
pBitmap: HBitmap;
begin
res := NffvUserGetHBitmap(_handle, pBitmap);
{получаем числовое значение картинки от _handle и pBitmap}
if (Failed(res)) then
begin
RaiseError('получить изображение невозможно.', res);
{при возникновении ошибки выводится сообщение «получить изображение невозможно»}
end;
nBitmap := TBitmap.Create;
nBitmap.Handle := pBitmap;
Result := nBitmap;
end;
И непосредственно сам обработчик.
Листинг 14.
Код:
procedure TForm2.Button1Click(Sender: TObject);
begin
Engine := nil;
_allModuleString := GetAvailableScannerModules;
try
Engine := TNffv.Create('fingers.db', '', 'Futronic'); //Подключаем наш //сканер
Engine.RemoveUsers; //Чистим базу
_user := Engine.Enroll(2000, _engineStatus); //Сканируем //отпечаток
if (_engineStatus = nfesTemplateCreated) then //Если //сосканировался делаем дальше
begin
Bitmap := _user.GetImage; //Получаем картинку и пишем ее в //имадж
randomize;
bitmap.SaveToFile(Edit1.Text); //сохраняем ее в папке с проэктом с //именем пользователя
Engine.RemoveUsers;
FreeAndNil(Engine); //очищаем память движка
ShowMessage(‘… Done’);
end
else {какой error} ;
except
MessageDlg(‘невозможно запустить сканер или создать/загрузить базу данных.’ + #13#10 +
‘пожалуйста проверьте:’ + #13#10 + ‘ – правильно ли введён пароль;’ + #13#10 + ‘ – правильность названия базы данных;’ + #13#10 +‘ – правильно ли подключен сканер.’, mtError, [mbOK], 0);
FreeAndNil(Engine);
Exit;
end;
end;
Рассмотреть типовые решения применения сканера отпечатков пальцев;
Типовыми решениями могут быть: реализация многофакторной аутентификации, доступ к информации\ресурсу, физический доступ, интеграция со сторонней б\д для поиска информации о человеке, и тд. Забыл добавить, драйвера для корректной работы fs80 с помощью самописной софтины либо SDK подходят не все, надо экспериментировать, какие-то находил рабочие.
Если у кого будут вопросы консультационного характера, с радостью отвечу на все вопросы.
Последнее редактирование модератором: