Image alt

Как работает «невидимая капча»: история разработки Yandex SmartCaptcha

Рассказываем, как из внутренней капчи Яндекса появился сервис SmartCaptcha. Для капчи можно использовать гибкие настройки и дизайн, подходящий для любого сайта.

Меня зовут Алексей Тощаков, я руковожу службой антифрода в Яндексе. Моя команда использует алгоритмы и данные для защиты наших сервисов и пользователей от фрода. Один из сервисов, которые мы разрабатываем, — SmartCaptcha в Yandex Cloud. В 2021 году мы уже рассказывали на Хабре про то, как изменялась и совершенствовалась наша внутренняя капча. После публикации статьи к нам стали обращаться владельцы сайтов с вопросами о том, как можно наш инструмент использовать у них. Это стало для нас сигналом, что пора взяться за разработку сервиса Yandex SmartCaptcha для внешних клиентов. Казалось бы, весь код уже написан, так что нужно только оформить лендинг и подключить биллинг. В реальности вышло сложнее — нам пришлось провести рефакторинг кода и многое изменить. Сейчас расскажу подробнее о том, что получилось в итоге.

Начало разработки: кто тут робот, а кто — человек

Первая задача на разработку внешнего сервиса капчи появилась ещё в декабре 2020 года. И уже в тот момент было понятно, что просто так это сделать не получится. Мы общались с потенциальными клиентами, узнавали об их запросах и погружались глубже в проблемы: кому-то была необходима кастомизация внешнего вида капчи, чтобы она хорошо вписывалась в дизайн веб-страницы, кто-то хотел «невидимую капчу» — проверку, незаметную для посетителей сайта. Отдельным компаниям, у которых бизнес был построен вокруг мобильных приложений, нужна была капча для мобильных, также был запрос на добавление выбора языка.

Сервис должен не просто принимать решения о похожести пользователя на робота или человека, а работать так, чтобы одновременно защищать сайты и не раздражать людей. А значит, нам предстояло много работать как над качеством сервиса, так и над его внешним видом, сложностью и множеством других параметров.

Мы давно используем технологию капчи внутри Яндекса и, конечно, работаем с метриками для оценки качества её работы. Было важно, чтобы высокое качество работы внутреннего сервиса сохранилось и для внешнего. В теории это стандартная задача бинарной классификации. Поэтому мы использовали обычные полноту и точность, а затем искали баланс между ними.

Однако самая большая проблема — понять, кто на самом деле робот, а кто человек. Один раз капча может сделать это верно, а уже через пять минут «обозвать» человека роботом и заставить его вводить слова. Это всех раздражает. По нашей статистике видно, что больше половины людей проходят нашу капчу без необходимости вводить текст, им достаточно поставить галочку. Это уже показалось довольно неплохим результатом, но мы продолжили работу: улучшали качество алгоритмов, модифицировали ML-модели, работали над разделением уровня сложности капчи в зависимости от решения модели.

«Невидимая капча» и рефакторинг

Многие владельцы сайтов не хотят отвлекать внимание пользователей на дополнительные действия вроде нажатия галочки «Я не робот». При этом сама проверка на сайте нужна. Поэтому мы получали запросы на так называемую «невидимую капчу». Это способ подключения сервиса, при котором пользователь в нормальных условиях не видит кнопку «Я не робот». Окно с заданием покажут только тем, кого виджет SmartCaptcha посчитает подозрительными. Нашим следующим шагом стала работа над «невидимой» версией сервиса. В идеале пользователь не должен видеть кнопку «Я не робот». Проверка должна проходить «под капотом» (кнопки нет, но всё работает так же, как если бы сам пользователь нажал на неё). Если алгоритмы распознали вас как человека, капча действительно будет невидимой. А подозрительным пользователям будет показан блок с текстом на картинке.
Задача выглядела просто: делаем блок «Я не робот» невидимым на странице и просто программно вызываем нажатие на кнопку. Но если мы это умеем, то зачем вообще заставлять людей в каких-то случаях проходить «мучения» с вводом символов? Напрашивался ответ, что видимая капча нам и вовсе не нужна, и можно было бы оставить только невидимый вариант.

Image alt

«Невидимая капча» — понять, что проходишь капчу, можно только по плашке в правом нижнем углу

Но если пользователь подозрительный, для него после невидимого этапа неожиданно посреди страницы сайта появится окно, где его попросят ввести два слова. Для кого-то из владельцев сайтов такой вариант не подходит, потому что внезапное появления капчи вызывает недоумение и разочарование у пользователей. При этом, если рядом с кнопкой «Войти» человек видит кнопку «Я не робот», он понимает, что проходит капчу, и предложение ввести два слова с картинки в отдельном окне уже не кажется таким внезапным.

Image alt

Пример видимого блока «Я не робот»

В итоге оба варианта оказались востребованы, поэтому мы решили поддерживать разные форматы капчи, чтобы у клиентов была возможность выбора и гибкой настройки.

Видимый:


<Блок кода видимой капчи>


<script  src="https://captcha-api.yandex.ru/captcha.js?render=onload&onload=onloadFunction"
  defer
></script>


<script>
  function onloadFunction() {
    if (!window.smartCaptcha) {
      return;
    }

    window.smartCaptcha.render('captcha-container', {
      sitekey: '<Ключ_для_клиентской_части>',
      callback: callback,
    });
  }


  function callback(token) {
    console.log(token);
  }
</script>


<form id="form">
  <div id="captcha-container"></div>
</form>          

Невидимый:


<Блок со скриптом для невидимой капчи>


<script  src="https://captcha-api.yandex.ru/captcha.js?render=onload&onload=onloadFunction"
  defer
></script>

<script>
  function onloadFunction() {
    if (!window.smartCaptcha) {
      return;
    }

    window.smartCaptcha.render('captcha-container', {
      sitekey: '<Ключ_для_клиентской_части>',
      invisible: true, // Сделать капчу невидимой
      callback: callback,
    });
  }

  function callback(token) {
    console.log(token);
  }

  function handleSubmit() {
    if (!window.smartCaptcha) {
      return;
    }

    window.smartCaptcha.execute();
  }
</script>


<form id="form">
  <div id="captcha-container"></div>
 <input type="submit" onsubmit="handleSubmit()" />
</form>

Оба варианта для разработчика практически ничем не отличаются — в невидимом варианте капчи нужно лишь немного изменить код для её появления. Но даже такое небольшое изменение привело к частичному рефакторингу архитектуры. Дальше расскажу, почему это потребовалось.

Изначально капча представляла собой два React-приложения:

  • Captcha — в нём находился код для кнопки «Я не робот», формы с заданием и одного хука useCaptchaState, который содержал всю логику капчи.

  • Antirobot — собирался из запчастей из Captcha с необходимыми обвязками и нуждами для конкретного сервиса Яндекса.

Когда началась разработка сервиса Yandex SmartCaptcha, мы постарались переиспользовать код, который уже был написан. Например, хук useCaptchaState, помимо логики самого виджета, контролировал загрузку ресурсов, сбор фингерпринтов пользователя и использовался в Antirobot. Это привело нас к первой версии архитектуры.

Скрипт находится на стороне пользователя и загружает два iframe: один с кнопкой «Я не робот» и хуком useCaptchaState, второй — с заданием. Все это связано небольшой библиотекой поверх postMessage. В такой архитектуре логика находилась в первом iframe, а второй был придаточным — он просто отрисовывал данные, полученные из другого окна.

Image alt

Архитектура прекрасно работала для видимой капчи, но абсолютно не подходила для невидимой. Нам приходилось загружать для пользователей избыточный javascript и html-код, относящиеся к кнопке «Я не робот». Появилась идея перенести useCaptchaState в iframe с заданием. В таком варианте получалась готовая невидимая капча. Если понадобится видимая, то нужно было просто дорисовать кнопку «Я не робот».

Image alt

Однако и это решение было не лучшим. Мы планировали, чтобы реальные пользователи проходили капчу уже на стадии нажатия кнопки «Я не робот». Это больше 50% посетителей сайтов. Для нас неприемлемо было нагружать такое большое количество людей неиспользуемым трафиком — в худшем случае 300 КБ.

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

Кастомизация капчи

Для капчи очень важным оказался её внешний вид. Нельзя было просто выложить сервис с дизайном, который заточен под страницы Яндекса.

Image alt

Светлая капча на тёмном фоне плохо выглядит

Хотелось попробовать малой кровью добавить кастомизацию внешнего вида. Для начала мы разработали возможность настроить произвольные CSS-токены:

{
    "checkboxBackgroundColor": "none",
    "checkboxBorder": "1px solid #d9d9d9",
    "checkboxBorderRadius": "6px",
    "checkboxFirstLetterColor": "#f00",
    "checkboxPrimaryTextColor": "#000",
    "checkboxSecondaryTextColor": "rgba(0, 0, 0, 0.6)",
    …
}

И получили желаемый результат:

Image alt

Но в наши дни отдавать пользователям сервис, настраивать внешний вид которого придётся через CSS-токены, совершенно неправильно. Это неудобно, требует от пользователя вслепую экспериментировать с различными параметрами и не даёт возможности сразу оценить визуальные изменения. Чтобы понять, как совместить гибкость возможных настроек с удобством их подбора, мы несколько раз обсудили разные варианты с партнёрами-разработчиками и нашими дизайнерами, сгруппировав в итоге CSS-токены в несколько понятных групп:

Image alt

Более того, после запуска сервиса мы быстро поняли, что пользователям нужно дать пресеты. То есть несколько самых популярных настроек внешнего вида.

Image alt

Выглядит гораздо удобнее, но пока ещё не идеально. Надеемся, что этот инструмент поможет привнести в мир больше стильно выглядящих капч, и они станут полноценными элементами внешнего вида сайта, а не отдельно стоящими неказистыми «заборами» перед пользователями.

Настройка сложности капчи

Теперь к самому интересному и самому раздражающему. Капча вида «введите текст с картинки» — довольно старая придумка по меркам интернета. При этом она по-прежнему выполняет свою задачу. Не обязательно заставлять пользователя выбирать фотографии львов или катеров — в меру сложный текст справится с отсеиванием ботов. Наша капча не использует готовые фото текста, сервис сам генерирует произвольные словосочетания. Именно такой текст даёт возможность поддерживать уровень распознавания человеком 85% и затрудняет распознавание капчи ботами. Также подробно про картинки с текстом мы писали в предыдущей статье.

Как у любого технического решения, у текстовой капчи есть преимущества и недостатки. К сильным сторонам можно отнести простую и понятную формулировку задачи перед пользователем, а также лёгкость реализации решения, что делает её доступной широкому кругу людей. Минус в том, что повышение сложности распознавания текста роботами очень сильно коррелирует с увеличением сложности и для людей. Грубо говоря, если роботу сложно «прочитать» текст, то и у человека могут возникнуть трудности.

Нужно было дать возможность управлять этой сложностью и находить компромисс между тем, сколько роботов будет успешно справляться с распознаванием текста на картинках, и тем, насколько сложной будет жизнь реальных людей. После того, как мы взялись за эту задачу, стало понятно, что можно сформировать различные кластеры картинок с учётом того, насколько сильно при распознавании текста на них ошибаются люди и роботы.

Поэкспериментировав с разными разбиениями, мы пришли к неутешительным выводам. Между людьми и специально заточенными на прохождении капчи роботами сложно найти достаточную границу, которая бы делала такое разделение оправданным. Кроме того, такая кластеризация требует накопления исторических данных и не позволяет получить новые картинки здесь и сейчас.

Анализ одной только статистики по картинкам не дал хороших результатов, поэтому мы сделали шаг назад и посмотрели на аспекты работы алгоритма генерации подобных картинок. Сопоставив это с данными об успешности людей и роботов в распознавании, мы пришли к ожидаемому выводу: чем сильнее искривлён текст на картинке, тем сложнее распознать его людям и роботам. Ожидаемо, большее искривление написанного текста ухудшает его распознавание как у роботов, так и у людей, но нашлись и другие параметры, влияющие на успех.

Аналитика аналитикой, но всё же стоит иногда смотреть глазами и удостоверяться, что мы действительно делаем что-то полезное. В нашем случае получились такие картинки:

Image alt

Лёгкая, средняя и сложная капчи

Приятно, когда ожидания совпадают с реальностью.

Капча для мобильных приложений

Среди запросов от потенциальных клиентов, как я писал выше, была и ожидаемая задача сделать капчу и для мобильных приложений. Мы решили адаптировать сервис для работы с WebView и написать подробный гайд о том, как удобно (для разработчиков и пользователей приложений) встроить капчу в мобильные приложения.

Image alt
Image alt

Что в итоге

В начале разработки капчи мы думали, что у нас уже есть готовый продукт, которому не хватает разве что системы для приёма оплаты. Всё оказалось гораздо сложнее. Запустить и развивать внешний продукт, даже имея хорошую технологию, не так просто. Сервис SmartCaptcha отличается от внутренней капчи: мы добавили новые возможности, провели рефакторинг кода и продолжаем вносить изменения.

Защитите свой сайт от нежелательных роботов с помощью Yandex SmartCaptcha. Вы можете выбрать формат, который идеально подходит для вашего сайта, и легко вставить готовый код. Первые 250 тысяч запросов в месяц абсолютно бесплатно!

Yandex SmartCaptcha

Напишите нам

Начать пользоваться Yandex Cloud

Тарифы

Узнать цены и рассчитать стоимость

Мероприятия

Календарь событий Yandex Cloud
Как работает «невидимая капча»: история разработки Yandex SmartCaptcha