Решаем hcaptcha
У hcaptcha вновь изменения. На этот раз изменился способ обработки ответа.
Введение
Немного хронологии. Когда hcaptcha появилась, то требовала следующих действий:
- заполнить g-recaptcha-response-{salt}
- h-captcha-response-{salt}, salt выдергивался у iframe из аттрибута data-hcaptcha-widget-id
- функция grecaptcha.getResponse должна была возвращать токен
Через 2 месяца пришлось заменить document.getElementById("hcaptcha_submit").click();
на ..."challenge-form").submit();
Еще через 3 месяца наконец убрали рудименты g-recaptcha
Т.е. что получилось, ключ сайта отправлялся на сервис распознавания, токен, полученный в результате, вставлялся в textarea и форма сабмитилась. А теперь Cloudflare поменяла способ взаимодействия с сервисом и таким способом капчу больше не пройти. Все что произойдет это перезагрузка страницы.
Ищем решение
После долгого и мучительного изучения исходников скриптов, я написал в тех поддержку сервиса распознавания. На что мне дали ссылку на их блог, в котором они упомянули о своем дополнении и описали решение.
Так как мне не подходит “используйте наше дополнение”, так оно еще и для хрома. И я решил скачать файл дополнения из магазина аддонов и надеялся, что это будет архив.
Однако магазин хрома не дает возможности получить прямую ссылку на файл. Во всяком случае из окна FF.
Зато в магазине есть дополнение для скачивания других дополнений Chrome extension source viewer. Код которого есть на гитхабе. И там мы узнаем как именно генерируется ссылка на загрузку дополнения
- [PRODVERSION] — это версия браузера, я взял последнюю
- [EXTENSIONID] — это ID расширения
Подробнее тут https://github.com/Rob--W/crxviewer/blob/master/src/cws_pattern.js#L134
Дополнения имеет расширение crx и это не переименованный zip, а application/x-chrome-extension, хотя тоже архив и открывается менеджером архивов GNOME.
Внутри, по пути /content/captcha/hcaptcha/interceptor.js, как раз таки находится скрипт выполняющий рекомендации описанные в блоге. А именно, дефайнит объект hcaptcha до того как это сделает скрипт hcaptcha-challenge.js.
Я взял эту монструозную конструкцию и создал новый скрипт в Tampermonkey (это такое дополнение для FF)
// ==UserScript==
// @name New Userscript
// @namespace http://tampermonkey.net/
// @version 0.1
// @description try to take over the world!
// @author You
// @match http://site.com/
// @icon https://www.google.com/s2/favicons?domain=site.com
// @grant none
// ==/UserScript==
(function() {
'use strict';
window.location = "javascript: (" + function () {
let hCaptchaInstance;
Object.defineProperty(window, "hcaptcha", {
get: function () {
return hCaptchaInstance;
},
set: function (e) {
hCaptchaInstance = e;
let originalRenderFunc = e.render;
hCaptchaInstance.render = function (container, opts) {
createHCaptchaWidget(container, opts);
return originalRenderFunc(container, opts);
};
},
});
let createHCaptchaWidget = function (container, opts) {
if (opts.callback !== undefined && typeof opts.callback === "function") {
let key = "hcaptchaCallback" + Date.now();
window[key] = opts.callback;
opts.callback = key;
}
let widgetInfo = {
captchaType: "hcaptcha",
widgetId: 0,
containerId: container,
sitekey: opts.sitekey,
callback: opts.callback,
};
console.log(widgetInfo);
}
} + ")()";
})();
Теперь, после перезагрузки страницы я наконец получаю callback функцию, которую так давно искал.

Если просто ввести имя колбека в консоль, то будет видно определение. Функция принимает аргумент i и нужно выяснить требуется ли его указывать. А то в этом JS хрен поймешь какие аргументы нужно какие не нужно, как на нем вообще кто-то пишет!?
В той же директории, в файле processor.js находим метод onSolved, в котором написано куда нужно засунуть свой ответ.
onSolved: function(widget, answer) {
let container = $("#" + widget.containerId);
container.find("textarea").val(answer);
container.find("iframe").attr("data-hcaptcha-response", answer);
},
Ну тут все понятно, потому что в контейнере только один iframe и только один textarea

Идем дальше и ищем кто использует метод onSolved. Спасибо огромное разработчикам JetBrains за великолепную функцию Find usages.
Я нашел единственное место
function doActionsOnSuccess(msg) {
...
processor.onSolved(widget, msg.response.code);
Config.getAll().then(config => {
let callback = processor.getCallback(widget);
if (callback) {
location.href = `javascript: window["${callback}"]("${msg.response.code}")`;
}
Опасненько, msg.response.code это не HTTP Status Code, а answer, потому что onSolved принимает answer, что является токеном решения капчи.
Все эти изыскания говорят о том, что для решения задачи нужно:
- заполнить токен в textarea[@id=’h-captcha-response-{salt}’]
- заполнить токен в аттрибут data-hcaptcha-response в iframe
- вызвать колбек и передать ему токен
Для проверки я написал скрипт который решает капчу и заполняет токен.
Результат? Все работает.
Значит теперь решать капчу невозможно без сторонних компонент, таких как дополнение, которое будет выполнять инъекции. О создании такого дополнения.