YAML Metadata Warning:empty or missing yaml metadata in repo card
Check out the documentation for more information.
ZkFingerAgent
Локальный агент ZKTeco для веб-приложений — читает отпечаток пальца из браузера
Local ZKTeco bridge that lets a web page read fingerprints from a USB scanner
Зачем это нужно
Браузер не имеет доступа к USB-устройствам. Веб-страница не может напрямую обратиться к сканеру отпечатков — ей нужен посредник, работающий на том же компьютере.
ZkFingerAgent — этот посредник. Он поднимает небольшой HTTP-сервер на
localhost:8088, разговаривает со сканером ZKTeco через libzkfp.dll
и отдаёт странице готовый шаблон отпечатка.
┌──────────────┐ fetch() ┌──────────────────┐ ctypes ┌────────────┐
│ Веб-станица │ ───────────► │ ZkFingerAgent │ ──────────► │ ZK4500 │
│ (браузер) │ ◄─────────── │ localhost:8088 │ ◄────────── │ USB │
└──────────────┘ JSON └──────────────────┘ libzkfp └────────────┘
Возможности
| Одна копия на компьютер | Порт и сканер может держать только один процесс. Повторный запуск не плодит агентов: новая копия показывает окно уже работающей и выходит |
| Точная диагностика | Различает «нет SDK», «сканер не в USB», «драйвер сломан», «библиотека не той разрядности» — и под каждую беду даёт кнопку с нужным действием |
| Установка SDK из программы | Находит дистрибутив, распаковывает, выбирает библиотеку нужной разрядности по PE-заголовку, ставит драйвер |
| Живое изображение отпечатка | Картинка с сенсора после захвата, с анимацией сканирования |
| Регистрация эталона | Три захвата объединяются в регистрационный шаблон через ZKFPM_DBMerge |
| IPv4 и IPv6 | Слушает 127.0.0.1 и [::1]: браузер обращается к localhost, а тот резолвится по-разному |
| Работа в трее | Автозапуск с Windows, сворачивание в трей, журнал всех обращений страницы |
| Диагностика блокировок браузера | Замечает, что страница открыта, но до агента ничего не дошло, и предлагает решение |
| Защита паролем 🆕 | При запуске программа показывает только карточку «работает в фоновом режиме». Настройки, журнал и остановка агента открываются лишь после входа по паролю. Сам пароль не хранится — только соль и PBKDF2-хеш |
| Запрет удаления файлов 🆕 | Права NTFS на папку программы: обычный пользователь запускает и работает, но не может удалить или подменить exe. Снять защиту можно только с правами администратора (UAC) |
| Отказоустойчивый конфиг | Настройки скрыты и продублированы на системном диске: удалили один agent.config.json — программа поднимется из копии и восстановит его |
| Мгновенная реакция на USB 🆕 | Сканер вынули или вставили — программа замечает это за секунду и сама переоткрывает устройство. Состояние берётся у Windows через cfgmgr32, а не у SDK: тот держит список устройств с момента инициализации и на вынутом сканере продолжает отвечать «всё на месте» |
| Полное удаление 🆕 | Кнопка в настройках стирает exe, оба конфига, папку в C:\ProgramData и запись автозапуска. Драйвер сканера и SDK остаются: их ставил отдельный установщик |
Установка
- Скачайте
ZkFingerAgent.exe— один файл, ничего распаковывать не нужно. - Запустите. Установщик ZKFinger SDK встроен внутрь: кнопка «Установить SDK» → «Установить».
- Переподключите сканер к USB после установки драйвера — Windows опознала его ещё со старым драйвером.
- В «Настройках» укажите адрес страницы входа вашего сайта.
Готово: карточка «Сканер» покажет разрешение и dpi, агент начнёт отвечать странице.
API
Страница обращается к агенту тремя запросами:
| Запрос | Ответ |
|---|---|
GET /api/machine/id |
{"machineId": "..."} |
GET /api/fingerprint/scan |
{"success": true, "hash": "...", "template": "...", "length": 1134} |
GET /api/health |
Полная диагностика: сканер, SDK, адреса, счётчик обращений |
Если сканера нет в USB, /api/fingerprint/scan отвечает 503 сразу, не выжидая
таймаут: странице незачем ждать палец, который некуда приложить.
Пример клиентского кода:
const AGENT = 'http://localhost:8088';
const res = await fetch(`${AGENT}/api/fingerprint/scan`,
{ signal: AbortSignal.timeout(20000) });
const data = await res.json();
if (data.success) {
form.fingerprintHash.value = data.hash;
form.fingerprintTemplate.value = data.template;
}
Проверить состояние агента:
curl http://localhost:8088/api/health
Скриншоты
![]() |
![]() |
| Настройки порт, формат шаблона, идентификатор компьютера |
Доступ из браузера проверка связи и обход блокировки |
Защита 🆕
Программу на общем компьютере можно закрыть паролем, чтобы посторонний не менял настройки, не останавливал агент и не удалял его файлы. Защита состоит из двух независимых замков — их включают галочками в Настройках → «Защита».
1. Пароль на вход. Когда защита включена, при запуске программа не показывает главное окно. Вместо него — маленькая карточка «работает в фоновом режиме»: агент уже поднят и отвечает странице, окно можно спокойно закрыть. Кнопка «Вход в систему» открывает окно пароля, и только после верного пароля появляется полный интерфейс.
![]() |
![]() |
| Фоновый режим агент работает, окно закрыто, вход по паролю |
Вход в систему показать/скрыть пароль, защита от перебора |
- Сам пароль нигде не хранится — в
agent.config.jsonлежат только случайная соль иPBKDF2-HMAC-SHA256(120 000 итераций). Сверка — постоянная по времени, чтобы пароль нельзя было подобрать по скорости ответа. - После пяти неверных попыток окно входа замолкает на 30 секунд.
- Пароль спрашивается и при остановке агента, и при выходе из программы.
2. Запрет удаления файлов. Отдельная галочка выставляет права NTFS на папку программы: группе «Пользователи» остаётся чтение, запуск и запись (агент по-прежнему пишет свой конфиг и журнал), но не остаётся права на удаление — ни файлов, ни самой папки. Владельцем папки становятся «Администраторы». Изменение прав идёт через запрос UAC и выполняется в фоне — окно не подвисает.
3. Защита самого конфига. agent.config.json помечается скрытым и
хранится в двух местах сразу — рядом с exe и в копии на системном диске
(C:\ProgramData\ZkFingerAgent). Если один файл случайно удалят, программа
поднимется из второго и восстановит утраченный. Галочка «Запретить удаление»
защищает обе папки одним запросом администратора, так что оба конфига
становятся неудаляемыми, но программа по-прежнему может их перезаписывать.
Настройки
| Параметр | Смысл |
|---|---|
port, host |
Адрес агента, по умолчанию 127.0.0.1:8088 |
site_url |
Адрес страницы входа |
scan_timeout |
Сколько ждать палец |
template_format |
base64 или hex |
hash_algo |
sha256, sha1, md5 |
machine_id_source |
UUID платы, MachineGuid или имя ПК |
require_scanner |
Не сообщать сайту о готовности, пока сканер не готов |
include_image |
Прикладывать BMP отпечатка к ответу |
protect_enabled 🆕 |
Закрывать программу паролем при запуске |
password_salt, password_hash 🆕 |
Соль и PBKDF2-хеш пароля — сам пароль не хранится |
Хранятся в скрытом agent.config.json рядом с exe и в копии на системном
диске (C:\ProgramData\ZkFingerAgent) — на случай, если один файл удалят.
Если браузер блокирует агент
Программа работает, сканер готов, а страница пишет, что агент недоступен. В консоли браузера (F12):
Access to fetch at 'http://localhost:8088/api/machine/id'
has been blocked by CORS policy: The request client is not
a secure context and the resource is in more-private
address space `loopback`.
Chrome не разрешает странице, открытой по обычному http, обращаться
к локальным адресам. Запрос блокируется до отправки — до агента он
не доходит, и заголовками ответа это не лечится.
Кнопка «Доступ из браузера» даёт три инструмента:
- Проверка связи — открывает сайт и считает реальные обращения. Ответ однозначный, а не догадка.
- Запуск с доступом — браузер стартует с ключом
--unsafely-treat-insecure-origin-as-secure. Без прав администратора, реестр не трогается. Рядом — создание ярлыка на рабочий стол. - Политика браузера —
OverrideSecurityRestrictionsOnInsecureOriginи политики Local Network Access для Chrome и Edge, только для вашего адреса. Есть экспорт.regдля групповых политик домена.
Правильное решение — перевести сайт на HTTPS. Защищённой странице доступ
к localhost разрешён, и ничего настраивать на компьютерах не нужно.
Важно для серверной части
Шаблон отпечатка не одинаков при каждом сканировании. Палец лежит под
другим углом, с другим нажимом — байты выходят разные, и хеш каждый раз новый.
Сравнение hash == hash_из_БД не совпадёт практически никогда.
Отпечатки сравнивают алгоритмом сопоставления: ZKFPM_DBMatch возвращает
score, порог обычно 55. Сервер должен брать template и прогонять его через
SDK против сохранённых шаблонов. Поле hash годится как метка, но не как
способ аутентификации.
Поддерживаемое оборудование
| Сканеры | ZK4500, ZK9500, ZK6500, ZK8500R, SLK20R и совместимые |
| SDK | ZKFinger Standard SDK 5.x (libzkfp.dll) |
| ОС | Windows 10, Windows 11 (x64) |
Проверено на ZK4500 (USB\VID_1B55&PID_0840), ZKFinger SDK 5.3.0.33,
изображение 288×368 при 500 dpi. Сигнатуры ctypes сверены с libzkfp.h
из состава SDK.
Технологии
Python 3.14 · PySide6 (Qt 6) · ctypes FFI к нативной libzkfp.dll ·
http.server · PyInstaller · Windows Registry API
English
What it is
Browsers cannot talk to USB devices. A web page has no way to reach a fingerprint scanner directly — it needs a helper running on the same machine.
ZkFingerAgent is that helper. It runs a small HTTP server on localhost:8088,
talks to the ZKTeco scanner through libzkfp.dll and hands the page a ready
fingerprint template.
Quick start
- Download
ZkFingerAgent.exe— a single file, nothing to unpack. - Run it. The ZKFinger SDK installer is bundled inside: Install SDK → Install.
- Replug the scanner after the driver install.
- Set your login page address in Settings.
API
| Endpoint | Response |
|---|---|
GET /api/machine/id |
{"machineId": "..."} |
GET /api/fingerprint/scan |
{"success": true, "hash": "...", "template": "..."} |
GET /api/health |
Full diagnostics |
Notable engineering details
- Single instance enforced with a named mutex; a second launch surfaces the running window instead of fighting over the port and the device.
- Dual-stack listener on
127.0.0.1and[::1]—localhostresolves to IPv6 first on Windows, and browsers that do not fall back would otherwise fail. - DLL bitness detection reads the PE header
Machinefield to pick the rightlibzkfp.dllfor the running interpreter. - Honest diagnostics: distinct error codes for "SDK missing", "SDK not
loadable" and "device absent" — the SDK itself returns
-1for several of them. - Browser-block detection: the agent cannot see a blocked request, so it infers the block from silence after the site is opened.
- Hotplug that actually works: presence comes from
cfgmgr32, not fromZKFPM_GetDeviceCount— the SDK caches its device list at init and keeps reporting a device that was unplugged minutes ago. On top of the sub-millisecond bus snapshot polled once a second, the app registers for device-interface notifications:DBT_DEVICEARRIVALis not broadcast to plain windows for USB devices, only for volumes, which is why unregisteredWM_DEVICECHANGEhandlers appear to work until you test them with a real device. - Clean uninstall: one button wipes the exe, both config copies,
C:\ProgramData\ZkFingerAgentand the autostart entry. A running process cannot delete its own exe, so a detached script waits for the process to exit and finishes the job. The scanner driver and SDK are never touched.
Protection 🆕
On a shared PC the app can be locked so a bystander cannot change settings, stop the agent or delete its files. Two independent locks, toggled in Settings → Protection:
- Password lock. When enabled, launching the app shows only a small
"running in the background" card with a Sign in button — the agent is
already up and serving the page; the full window appears only after the
correct password. The password itself is never stored:
agent.config.jsonkeeps only a random salt and aPBKDF2-HMAC-SHA256hash (120k iterations), compared in constant time. Five wrong tries lock the prompt for 30 seconds. - Delete protection. NTFS permissions on the program folder let regular users run and use the app but not delete or replace the executable; the folder owner becomes Administrators. Changing this requires a UAC prompt and runs off the UI thread so the window never freezes.
- Fault-tolerant config.
agent.config.jsonis hidden and mirrored toC:\ProgramData\ZkFingerAgent. Delete either copy and the app boots from the other and restores the missing one; delete protection covers both folders.
Important for server developers
Fingerprint templates are not byte-identical between scans, so the hash
differs every time. Comparing hashes will never match. Use the matching
algorithm (ZKFPM_DBMatch, threshold ~55) against stored templates instead.
Tech stack
Python 3.14 · PySide6 (Qt 6) · ctypes FFI · http.server · PyInstaller



