Утечки персональных данных в России бьют все рекорды. За два года их совокупное число выросло – только вдумайтесь – в 40 раз. В 2021-м году таких инцидентов было всего четыре, в 2022 – свыше 140, а за первые семь месяцев 2023 года – уже 150.
Одним из возможных путей утечки является передача персональных данных на обработку третьей стороне. По сути, компания передает изображения удостоверяющих документов (паспортов или любых других) своих клиентов незнакомым людям, а что дальше происходит с данными – неизвестно.
Между тем, есть еще один способ ввода данных из документов – прямо на устройстве, без необходимости отправлять куда-то картинку. Он полностью исключает риск любой утечки. Речь идет о нашем мобильном SDK для распознавания паспорта. О том, как мы внедрили наш SDK в PWA (progressive web app), читайте под катом.
Прогрессивные веб приложения - технология, позволяющая сайту в браузере вашего смартфона стать похожим на нативное приложение. Иметь иконку на экране смартфона после установки, иметь сравнимую отзывчивость интерфейса, хранить кэш и “тяжёлые” с точки зрения скорости загрузки с сервера ресурсы прямо на клиентском устройстве и за счёт этого работать в офлайне, иметь доступ к железу и его вычислительным мощностям. Особым преимуществом PWA является избавление от необходимости размещаться в сторах приложений: если Android позволяет скачивать apk файл из любого источника, то в случае с iOS установить приложение можно только из официального стора, а PWA позволяют не заморачиваться с этим.
Вместе с популярностью PWA каждый год растёт и улучшается поддержка браузерами технологии WebAssembly. WebAssembly позволяет использовать движок браузера для выполнения тяжелых с точки зрения вычислений задач, в том числе по анализу изображений. Мы давно занимаемся развитием наших продуктов в этом направлении, благодаря WASM мы успешно распознаем баркоды, банковские карты, документы, счета, номера телефонов внутри браузера клиентского устройства. Работа с WASM позволяет вывести PWA на новый уровень.
PWA - это набор технических практик, среди которых нас сильнее всего интересует возможность кешировать ресурсы страницы: если размер модулей для распознавания баркодов, номеров телефонов и банковских карт позволяет (при хорошем качестве интернет-соединения) быстро загрузить их с сервера, то модули, позволяющие распознавать документы, удостоверяющие личность, лучше хранить на устройстве. Вот, например, сравнение модулей с разной функциональностью:
банковские карты и баркоды: 3.5mb gzip
Паспорт РФ, карты и баркоды: 8.3 gzip
Как видите, кеширование WASM-модуля вместе с остальными ресурсами позволяет обойтись без необходимости каждый раз загружать ~8Мб с сервера. Механизм PWA, позволяющий кешировать ресурсы, называется Service Worker. Когда ваш сайт запрашивает ресурсы с сервера даже при отсутствии интернета, Service Worker перехватывает запрос и решает что с ним делать: вернуть ресурс из кеша, запросить по API обновление, вывести сообщение и прочее. Этого достаточно, чтобы ваше приложение могло работать быстрее привычного сайта (если ресурсы возвращаются из локального кеша) и было полностью или частично автономно.
Давайте рассмотрим демо-пример для распознавания паспорта РФ, в котором мы поместим в локальный кэш клиентского браузера весь сайт вместе с WASM файлами, так, чтобы они теперь загружались из локального кеша всегда. Это позволит работать приложению полностью оффлайн. Наш демо сайт состоит из:
index.html - разметка страницы.
app.js - входной скрипт веб страницы. Работа с камерой, передача потока изображений с камеры, отображение результата.
worker.js - веб-воркер. Скрипт, принимающий поток изображений с камеры и работающий с WASM объектом.
sw.js - сервис-воркер. Скрипт самого сервис воркера.
moduleName.js - маленький скрипт, определяющий название WASM сборки для текущего браузера.
В главном скрипте веб страницы производим регистрацию сервисного воркера:
if ("serviceWorker" in navigator) {
navigator.serviceWorker.register("sw.js").then(
(registration) => {
console.log("Service worker registration succeeded:", registration);
},
(error) => {
console.error(Service worker registration failed: ${error});
}
);
} else {
console.error("Service workers are not supported.");
}
В сам сервис воркер мы передаем массив адресов, предназначенных для кэширования. Для удобства чтения поделим массивы адресов по группам. Сначала мы добавим сам путь к файлу главной веб-страницы, основной скрипт и веб-воркер ( именно это веб-воркер, обслуживающий WASM ).
// Files to cache
const cacheName = 'smartenginesWasm';
const appShellHtml = [
'sample.html',
'app.js',
'worker.js',
];
Потом опишем массив графических ресурсов веб-страницы. Пусть это будут иконки.
const appShellIcons = [
'assets/icons/icon-48x48.png',
'assets/icons/icon-72x72.png',
'assets/icons/icon-96x96.png',
];
А для кэширования WASM файлов нужно сделать небольшое отступление. WASM в принципе поддерживается сейчас почти в любом браузере, однако существует набор оптимизаций, по-разному поддерживаемый современными (в первую очередь мобильными) браузерами (мы писали об этом тут). Поэтому наши WASM модули делятся на три группы, каждая из которых подгружается в зависимости от технологических возможностей браузера. Для их определения через importScripts() мы подключаем крохотный файл, который возвращает нам имя сборки, подходящей для кэширования для текущей среды исполнения:
importScripts('./moduleName.js');
const module = await getModuleName();
const WasmUrls = {
'nosimd.nothreads': [
'bin/nosimd.nothreads/idengine_wasm.js',
'bin/nosimd.nothreads/idengine_wasm.wasm'
],
'simd.nothreads': [
'bin/simd.nothreads/idengine_wasm.js',
'bin/simd.nothreads/idengine_wasm.wasm'
],
'simd.threads': [
'bin/simd.threads/idengine_wasm.js',
'bin/simd.threads/idengine_wasm.wasm',
'bin/simd.threads/idengine_wasm.worker.js'
],
};
Далее мы собираем все адреса в единую кучку, и на событии, когда сервисный воркер готов, подсовываем ее ему:
const all = appShellHtml.concat(appShellIcons).concat(WasmUrls[module])
const cacheName = 'smartenginesWasm';
// Installing Service Worker
self.addEventListener('install', (e) => {
console.log('[Service Worker] Install');
e.waitUntil((async () => {
const cache = await caches.open(cacheName);
console.log('[Service Worker] Caching all: app shell and content');
await cache.addAll(all);
})());
});
Последним шагом является добавление события на прослушивание сетевых запросов.Если браузер начинает что-то запрашивать из сети, сервисный воркер сможет управлять этим:
// Fetching content using Service Worker
self.addEventListener('fetch', (e) => {
e.respondWith((async () => {
const r = await caches.match(e.request);
console.log([Service Worker] Fetching resource: ${e.request.url});
if (r) return r;
const response = await fetch(e.request);
const cache = await caches.open(cacheName);
console.log([Service Worker] Caching new resource: ${e.request.url});
cache.put(e.request, response.clone());
return response;
})());
});
})()
Теперь же, после первой загрузки веб страницы для ее последующей работы сетевое соединение больше не требуется. Работу подобного PWA-приложения мы продемонстрируем на видео.
Заключение
Распознавание документов прямо на устройстве, без необходимости куда-то отправлять картинку, имеет ряд неоспоримых преимуществ - всегда доступно, нет необходимости отправлять изображения с персональными данными пользователя на сторонние сервисы, результат распознавания всегда “полнее” и работа с ним гибче, есть возможность поднять качество распознавания, комбинируя результаты распознавания нескольких кадров. К тому же, с развитием различных фреймворков внедрение библиотеки распознавания становится всё проще - эта статья, иллюстрирующая внедрение в PWA, хороший тому пример.
В целом, современные PWA приложения могут во многом заменить классические приложения для мобильных телефонов в случаях когда приложению необходимо соединение с интернетом (допустим, это банковское приложение - вот свежее исследование на Forbes на эту тему). С развитием поддержки WebAssembly PWA будут получать всё больший функционал, всё сильнее приближаясь по удобству к нативным приложениям, позволяя обеспечить клиентам с разными мобильными ОС одинаковый пользовательский опыт и не уступающую обычным приложениям функциональность.