Упс! Не вдала спроба:(
Будь ласка, спробуйте ще раз.

Як я писав власний Reverse CAPTCHA сервіс, або чому класичні капчі бісять усіх, крім ботів

0
11 хвилин читання

Привіт, я Андрій, fullstack-розробник.

Останні кілька років я займався різними проєктами — робив конструктори для телеграм-ботів, B2B-системи, CRM, навіть великий HR-додаток. І щоразу, коли я стикався з необхідністю захистити форму або API від спаму, я вставляв ту саму капчу. І щоразу мене це дратувало. Не як розробника — як людину.

Я не знаю, як ви, але мене вже ну дуже дістало шукати на картинках пожежні гідранти. Особливо коли їх там немає. Або коли картинка піксельна. Або коли я клікаю правильно, а мені кажуть «спробуйте ще раз» і дають нову сітку з автобусами.

Це бісить.

А от що бісить ще більше — це коли класична капча, яка мала б зупиняти ботів, насправді не зупиняє сучасних ботів. Сучасному боту вже не обов’язково бути примітивним HTTP-скриптом. Він може працювати через браузер і використовувати vision-моделі для розпізнавання зображень Тобто виходить, що ми мучимо реальних людей, а від ботів цей захист майже не допомагає.

Я вирішив спробувати щось інше.

Спочатку це була просто ідея, а не продукт Чесно кажучи, я не планував робити стартап. Мені просто стало цікаво: а що, якщо перенести навантаження з мозку людини на процесор її пристрою? Що, якщо не питати «хто ти? доведи», а просто перевірити, чи здатен твій комп’ютер виконати криптографічне завдання? І зробити це максимально непомітно?

Тобто людина навіть не знає, що її перевіряють. Бо все відбувається у фоновому режимі.

Ця думка мене зачепила. Я сів і почав писати код.

Так з’явився KineSec.

Як це працює — спрощено Якщо пояснювати без зайвої технічної складності, то логіка така:

Власник сайту додає один скрипт — і все.

Коли користувач заходить на сторінку, віджет починає працювати «під капотом».

Як я писав власний Reverse CAPTCHA сервіс, або чому класичні капчі бісять усіх, крім ботів зображення 1

Він збирає мінімум даних про середовище і виконує невелике обчислювальне завдання.

Якщо пристрій справляється — формується токен.

Токен автоматично додається в усі форми на сторінці, і користувач навіть не помічає, що щось відбувалося.

Ви просто заходите на сайт, заповнюєте форму і відправляєте. Без світлофорів, без автобусів, без «виберіть всі квадрати».

Звучить ідеально, правда?

От тільки на практиці виявилося, що «просто» — це тільки на папері.

Перша проблема: динамічні форми З першого погляду все просто. Я написав такий код:

Підписуйтеся на наші соцмережі

javascript

const forms = document.querySelectorAll(’form’); forms.forEach(form => { const input = document.createElement(’input’); input.type = ’hidden’; input.name = ’kinesec_token’; input.value = token; form.appendChild(input); }); І воно працювало. На звичайних сайтах.

А потім я вирішив потестити на реальних проєктах, де використовується React. І там форми рендеряться динамічно. Через п’ять секунд після того, як скрипт завантажився, форми ще немає в DOM. А через десять — вона вже є, але мій скрипт про це не знає.

Тобто токен просто не потрапляв у форму. І користувач отримував помилку.

Я сидів і думав: «Ну класно, Андрію, ти зробив захист, який ламає користувацький досвід ще до того, як з’явився спам».

Рішенням став MutationObserver — штука, яка стежить за змінами в DOM. Як тільки на сторінці з’являється нова форма — скрипт одразу додає туди токен.

Це вирішило проблему. Але додало трошки сивини.

Друга проблема: користувач не хоче проходити перевірку двічі Якщо ви думаєте, що користувачі люблять капчу — ви помиляєтесь. Якщо ви думаєте, що вони люблять проходити її двічі за п’ять хвилин — ви дуже помиляєтесь.

Уявіть: людина зайшла на сайт, пройшла перевірку, перейшла на іншу сторінку — і знову бачить той самий віджет. Це бісить. Я би сам пішов з такого сайту.

Тому я додав кешування через localStorage.

Після успішної перевірки токен зберігається на 10 годин. Якщо користувач повертається на сайт або переходить на іншу сторінку — віджет бачить, що є дійсний токен, і навіть не показує себе.

Інтерфейс розблоковується миттєво. Ніяких затримок, ніяких дратівливих вікон.

Але тут важливий нюанс: localStorage — це не про безпеку. Це про зручність. Сервер все одно перевіряє кожен токен незалежно. Якщо хтось спробує підробити кеш — нічого не вийде, бо токен підписаний сервером.

Третя проблема: боти теж не дурні Якщо ви думаєте, що достатньо просто видавати токени і сподіватися, що боти не здогадаються їх підробити — ви помиляєтесь.

Боти теж вчаться. Сучасні парсери — це вже не просто скрипти, які роблять HTTP-запити. Це повноцінні автоматизовані браузери, які вміють виконувати JavaScript, рухати мишку, скролити сторінку — тобто імітувати людину майже ідеально.

Тому однієї «криптографічної задачі» недостатньо.

Я додав кілька рівнів:

JWT з підписом — токен підписується на сервері, і його не можна підробити без секретного ключа. Прив’язка до домену та IP — токен, згенерований для одного сайту, не працює на іншому. Rate limiting — якщо з однієї IP надто багато запитів, сервер починає їх відхиляти. Аналіз поведінки — рух миші, скрол, тапи. Але це, чесно кажучи, палиця з двома кінцями. Чому палиця з двома кінцями? Тому що аналіз поведінки — це еврістика. Вона не дає 100% гарантії. Іноді реальна людина поводиться «як бот» — швидко рухає мишкою або взагалі не рухає (наприклад, на мобільному пристрої). А іноді бот настільки добре імітує поведінку, що його майже не відрізнити.

Тому я не роблю ставку на один «магічний» показник. Я просто додаю кілька шарів захисту, щоб зробити парсинг дорожчим. Не непробивним — просто невигідним.

Найдивніша фіча: Anti-Human Це взагалі вийшло випадково.

Спочатку я думав тільки про те, як захистити сайт від ботів. Але потім я задумався: а бувають ситуації, коли боти бажані, а люди — ні?

І зрозумів, що бувають.

Наприклад, якщо ви робите API для ШІ-агентів або для автоматизованих систем. Ви хочете, щоб ваш сервіс використовували машини, а не люди, які створюють зайве навантаження або просто не є вашою цільовою аудиторією.

Тоді я додав режим Anti-Human. У ньому все працює навпаки: якщо система бачить занадто «людську» поведінку — рух мишки, скрол, кліки — вона блокує запит.

Звучить трохи абсурдно, правда? «Ми блокуємо людей, бо вони поводяться як люди».

Але технічно це просто інший набір правил.

Я не впевнений, що цей режим буде масово використовуватись. Але мені було цікаво його зробити. Як експеримент.

Про що я шкодую, або що б зробив інакше Якщо чесно, я багато чого не передбачив на старті.

Наприклад, я думав, що позиціонування віджета на сторінці — це дрібниця. А виявилося, що це одна з найбільших проблем. Якщо віджет плаває в кутку екрана — він перекриває кнопки на мобільних пристроях. Якщо він блокує екран — це дратує користувачів.

В результаті я зробив так: під час перевірки віджет створює напівпрозорий фон, блокує скрол і центрує себе посередині екрана. А після успішної перевірки — плавно зникає.

Це виглядає акуратно. Але я перероблював це тричі.

Ще я недооцінив складність роботи з чужими сайтами. Коли ти вбудовуєш свій код на чужий сайт — ти не контролюєш нічого. Ти не знаєш, які там стилі, які скрипти вже завантажені, яка версія браузера. Тому доводиться писати код, який працює в будь-яких умовах. І це складно.

Що я виніс із цього досвіду Перше — Vanilla JS це круто. Серйозно. Я звик працювати з React, де все декларативно, але коли пишеш на чистому JavaScript без фреймворків — починаєш краще розуміти, як насправді працює браузер. Це дає глибше розуміння. І віджет важить буквально кілька кілобайт.

Друге — UX важливіший за захист. Якщо ваша система захисту дратує користувачів — вони підуть. І неважливо, наскільки технічно досконалий ваш алгоритм.

Третє — безпека це завжди компроміс. Немає ідеального захисту. Є захист, який достатньо дорого обійти. І це нормально.

Четверте — не бійтесь експериментувати. Ідея Anti-Human може здатися безглуздою, але вона народилася з реальної потреби, яку я помітив. Можливо, вона не стане мейнстрімом, але вона розширила моє розуміння того, як взагалі можна будувати системи захисту.

Що далі Зараз KineSec працює на кількох продакшн-сайтах. Я збираю статистику, дивлюся, як поводяться користувачі, і думаю, що ще можна покращити.

У планах:

Додати WebSocket-аналітику в реальному часі. Розширити типи криптографічних завдань. Спробувати використовувати WebGL для додаткової перевірки. Якщо вам цікаво спробувати або потестувати мій захист — KineSec — Reverse CAPTCHA відкритий для реєстрацій: kinesec.recvik.com.ua

Але головне — я хочу почути думку інших розробників.

Як я писав власний Reverse CAPTCHA сервіс, або чому класичні капчі бісять усіх, крім ботів зображення 2

Тому, якщо ви теж стикалися з проблемою захисту від ботів або маєте досвід із власними CAPTCHA-рішеннями — пишіть у коментарях.

Як ви зараз захищаєте свої форми? Чи використовуєте старі добрі капчі з картинками? Чи вже перейшли на щось нове?

Обговоримо. Буду радий подискутувати.

Якщо ви хочете поділитися з читачами SPEKA власним досвідом, розповісти свою історію чи опублікувати колонку на важливу для вас тему, долучайтеся. Відтепер ви можете зареєструватися на сайті SPEKA і самостійно опублікувати свій пост.
0
Icon 0

Підписуйтеся на наші соцмережі