Коротка відповідь. Хто володіє приватним ключем і паролем до нього, той може підписати будь-що від імені власника — тому безпека електронного підпису починається з простого правила: ключ ніколи не повинен покидати браузер користувача. Передача ключа на сервер — найгірша з можливих архітектур, але й «просто в браузері» недостатньо: XSS на сторінці дістає і ключ, і пароль. Робоча модель — клієнтська криптографія в iframe на чужому origin: браузер сам, на рівні Same-Origin Policy, гарантує, що код сайту-хоста не бачить вміст віджета. Саме так побудовано DSTUcrypt: сайт передає дані для підпису — і отримує назад лише готовий signature.
Що насправді означає «завантажте ваш ключ» на сторонньому сайті
Приватний ключ КЕП — не просто файл. Це криптографічний еквівалент вашої особи або вашої компанії: юридично значущий підпис під договором, платіжним дорученням, звітністю чи довіреністю створюється саме ним. Ключ не знає меж і контексту — він однаково охоче підпише і документ, який ви бачите на екрані, і будь-який інший набір байтів, який йому подадуть.
Тому кожне «завантажте ваш ключ», яке ви бачите на сторонньому сайті, варто читати буквально: «передайте нам можливість підписувати будь-що від вашого імені». Питання лише в тому, кому саме й наскільки широко ця можливість відкривається. Це і є головний критерій, за яким слід порівнювати архітектури електронного підпису: хто, крім самого користувача, технічно здатен дістатися до ключа й пароля.
Ключ на сервері: найгірша модель
Найпростіша для розробника схема — прийняти файл ключа й пароль на бекенд і підписувати там. Вона ж — найнебезпечніша:
- Одна точка масового витоку. Злам сервера (або бекапів, або логів, куди «випадково» потрапив пароль) означає компроментацію ключів одразу всіх користувачів — не одного.
- Довіра людям, а не математиці. Адміністратор сервера, підрядник з доступом до бази, будь-хто в ланцюжку постачання може підписувати документи від імені користувачів — і жоден аудит логів не відрізнить такий підпис від справжнього, бо криптографічно він справжній.
- Користувач втрачає контроль. Він фізично не бачить, що саме підписується його ключем і скільки разів.
Саме тому «ключ ЕЦП на сервері» — антипатерн незалежно від того, наскільки надійним здається сам сервер: архітектура, у якій витік можливий, рано чи пізно ним закінчується.
Чому «просто в браузері» — теж мало
Логічний наступний крок — виконувати криптографію на клієнті: підключити JS-бібліотеку прямо на свою сторінку й підписувати локально. Ключ на сервер уже не їде — і це прогрес. Але тепер ключ і пароль живуть у тому самому JS-контексті, що й увесь інший код сторінки: ваші скрипти, аналітика, чат-віджети, рекламні теги, залежності з npm.
Один XSS — через уразливість вашого коду або скомпрометовану сторонню бібліотеку — і зловмисний скрипт читає файл ключа з інпута, перехоплює пароль із поля вводу й тихо відправляє їх собі. Для користувача все виглядає як звичайний підпис. Додайте сюди те, що сама чисто-JS реалізація ДСТУ-криптографії має відомі класи вразливостей — timing-атаки й помилки генерації одноразового k, що дозволяють відновити приватний ключ із самих підписів (докладно — у окремій статті), — і стає ясно: «в браузері» — необхідна умова, але не достатня. Потрібна ще й ізоляція всередині браузера.
Origin-ізоляція: криптографія в iframe чужого origin
У браузері вже є перевірений десятиліттями механізм ізоляції — Same-Origin Policy. Документи з різних origin-ів (схема + домен + порт) не мають доступу до DOM і даних одне одного. На цьому тримається вся безпека вебу: саме тому вкладка з випадковим сайтом не може прочитати вашу пошту в сусідній вкладці.
Клієнтська криптографія DSTUcrypt використовує цей механізм напряму: віджет підпису працює в iframe, завантаженому з origin dstucrypt.io — іншого, ніж сайт-хост. Користувач обирає файл ключа і вводить пароль усередині цього iframe, там-таки WebAssembly-ядро виконує всі обчислення. Жоден JS сайту-хоста — включно зі шкідливим, що потрапив туди через XSS, — не може зазирнути у вміст iframe: це гарантує не наш код і не «чесне слово», а сам браузер на рівні фундаментального механізму безпеки. Спілкування між сторінкою і віджетом іде лише через postMessage — вузький канал, яким ходять тільки дані операції та результат.
Чому файли віджета принципово не можна хостити в себе
Часте питання інтеграторів: «а можна скопіювати ваші файли й WASM на наш сервер?» Не можна — і це не комерційне обмеження, а принципова частина моделі безпеки. Уся описана вище ізоляція існує лише тому, що iframe завантажується з іншого origin. Якби файли віджета лежали на вашому сервері, iframe мав би ваш origin, межа origin-ів зникла б — і будь-який XSS на вашій сторінці знову читав би ключі й паролі користувачів, як у моделі «JS на самій сторінці».
Тому інтеграція зводиться до одного імпорту SDK з https://dstucrypt.io/embed/dstucrypt-embed.mjs: SDK сам будує адресу віджета зі свого origin, а ви нічого не хостите. Бонус цієї схеми — ви завжди на актуальній версії криптоядра без ручних оновлень: виправлення й нові можливості доїжджають до ваших користувачів автоматично.
Що отримує сайт-хост: лише готовий результат
Межу iframe перетинають лише дані, які у сайту і так є, та результат операції:
- Сторінка → iframe: байти документа й параметри підпису (формат, геш).
- iframe → сторінка: готовий підпис
signature, ідентифікатор сертифіката підписантаsignerCertIdі ознакаaccredited— чи виданий сертифікат акредитованим надавачем, тобто чи є підпис КЕП.
Байти ключа й пароль цю межу не перетинають ніколи — ні в бік сайту-хоста, ні в бік серверів DSTUcrypt. Обмін іде всередині браузера через postMessage: байти передаються напряму (ArrayBuffer), без проміжних base64-рядків і без мережевих запитів.
Три архітектури поруч: порівняння
| Критерій | Ключ на сервері | Криптографія в JS сторінки | Ізольований iframe-віджет |
|---|---|---|---|
| Витік при зламі сервера | масовий: витікають ключі всіх користувачів | масовий: підмінений JS сторінки краде ключі користувачів | ключів на серверах немає — красти нічого |
| Витік при XSS на сторінці | так: скрипт перехоплює ключ і пароль при завантаженні | так: ключ і пароль у тому ж JS-контексті | ні: Same-Origin Policy закриває вміст iframe від коду сторінки |
| Хто оновлює крипто | ваша команда, вручну | ваша команда, вручну (ризик застарілої бібліотеки) | DSTUcrypt, автоматично для всіх доменів |
Як це виглядає в коді
Уся архітектура ховається за кількома рядками на сторінці-хості — бекенд для підпису не потрібен узагалі:
підпис у ізольованому iframe — сторінка бачить лише результат
import { embed } from 'https://dstucrypt.io/embed/dstucrypt-embed.mjs';
// віджет відкривається в iframe на origin dstucrypt.io —
// ключ і пароль користувач вводить усередині нього
const signer = await embed('sign', { mount: 'modal' });
const { signature, signerCertId, accredited } = await signer.sign(fileBytes, {
format: 'CAdES-T',
});
// на сторінку повертається ЛИШЕ готовий результат — байти підпису
signature.download('document.p7s');
Геш підпису теж під контролем: за замовчуванням це ДСТУ ГОСТ 34.311-95 (саме його приймають держвалідатори), а сучасну «Купину» (ДСТУ 7564:2014) вмикає одна опція digest:'kupyna-256' — усе так само всередині ізольованого iframe.
Що дає DSTUcrypt поверх origin-ізоляції. Криптографію виконує не саморобний JS, а перевірене нативне C/C++-ядро, скомпільоване у WebAssembly: константний час операцій проти timing-атак, коректний одноразовий k у кожному підписі, ентропія з CSPRNG браузера. Мітки часу TSP та OCSP-статуси ходять через вбудований проксі з коробки, а оновлення криптоядра доїжджають до всіх інтеграцій автоматично — без релізів на вашому боці.
Поширені запитання
Чи бачить DSTUcrypt мій ключ або пароль?
Ні. Уся криптографія виконується у браузері користувача (WebAssembly) всередині iframe. Ключ і пароль вводяться в iframe і не передаються ні на сервери DSTUcrypt, ні на сайт, у який вбудовано віджет.
Чому віджети не можна розмістити на власному сервері?
Бо ізоляція ключа тримається на межі origin-ів: віджет виконується на origin dstucrypt.io, і Same-Origin Policy браузера не пускає код вашої сторінки до його вмісту. Якби файли лежали у вас, межа origin-ів зникла б — і будь-який XSS на вашій сторінці читав би ключі користувачів. Бонус: ви завжди на актуальній версії без ручних оновлень.
Що саме отримує мій сайт після підпису?
Лише готовий результат: байти підпису (signature), ідентифікатор сертифіката підписанта (signerCertId) і ознаку accredited — чи виданий сертифікат акредитованим надавачем, тобто чи це КЕП. Байти ключа й пароль межу iframe не перетинають ніколи.
Чи безпечний режим «запам'ятати ключ»?
Так, він спроєктований так, щоб ключ і далі не покидав браузер: контейнер зберігається зашифрованим (AES-GCM-256, ключ шифрування виводиться з PIN через PBKDF2-SHA256 із 310 тисячами ітерацій) у localStorage origin dstucrypt.io і жорстко прив'язаний до домену-хоста — сайт А не дістане ключ, збережений на сайті Б. PIN ніде не зберігається.
Читайте також
- Небезпека чисто-JS криптографії: timing-атаки, одноразовий k і відновлення ключа з підписів
- Як обрати рішення для КЕП: чек-лист для бізнесу
- Як додати КЕП на сайт за 10 хвилин: iframe-віджет без бекенду
КЕП на вашому сайті — без ключів на сервері
Готові віджети підпису, перевірки та шифрування за ДСТУ. Ключ і пароль не покидають браузер користувача. На кожному новому домені — 7 днів безкоштовно.
Живе демоЯк підключити