Как решить Cloudflare Turnstile в PHP с помощью Composer

solve cloudflare turnstile in php - How to Solve Cloudflare Turnstile in PHP With Composer

Cloudflare Turnstile встречается в двух формах, и для них нужен разный PHP-код. Виджет внутри формы — это вызов с двумя аргументами. Полностраничная промежуточная проверка требует ещё двух значений, считанных со страницы, а токен принимается только если вы отправляете обратно тот user agent, который использовало решение. Пропустите эту последнюю часть — и получите токен, который выглядит полностью действительным, но отклоняется каждый раз.

Какой из них перед вами?

ВиджетСтраница проверки
Внешний видЧекбокс в форме, которой вы всё ещё можете пользоватьсяПолностраничная заставка, всё заблокировано
Требуются cData и chlPageDataНетДа
Требуется возвращённый user agentНетДа

Наш живая демонстрация Turnstile запускает вариант с виджетом, что удобно для сравнения.

Настройка

# PHP 8.0+, with the curl and json extensions.
composer require capskip/capskip
use CapSkip\CapSkip;

$solver = new CapSkip([
    'host' => '127.0.0.1',
    'port' => 8080,
    'recaptchaTimeout' => 300,   // seconds, also covers Turnstile
]);

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

Режим виджета

$result = $solver->turnstile(
    '0x4AAAAAAA...',                 // the data-sitekey attribute
    'https://example.com/login'
);

echo $result['code'];                // cf-turnstile-response token

Поместите $result['code'] в cf-turnstile-response поле и отправьте форму. Вот и весь процесс для виджетов.

Страницам проверки нужны ещё два значения

Промежуточная проверка (interstitial) несёт состояние на уровне запроса, к которому привязан токен, и две его части должны передаваться вместе с решением:

  • cData, передаётся как data
  • chlPageData, передаётся как pagedata

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

$result = $solver->turnstile($sitekey, $pageUrl, [
    'data' => $cData,             // the cData value from the page
    'pagedata' => $chlPageData,   // the chlPageData value
    'action' => 'managed',        // optional, when the page declares one
]);

echo $result['code'];
echo $result['userAgent'];        // needed for the submit

User agent здесь обязателен

Turnstile привязывает токен к отпечатку браузера, который его создал, а user agent — часть этого отпечатка. CapSkip возвращает тот, который он использовал в $result['userAgent']. Отправьте запрос с user agent по умолчанию от cURL — и Cloudflare отклонит токен, который в остальном совершенно исправен.

userAgent заполняется только для Turnstile. Он отсутствует для всех остальных типов капчи, и именно поэтому на этом попадаются те, кто повторно использует рабочий помощник для reCAPTCHA.

$ch = curl_init($pageUrl);

curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    // Send back the exact user agent the solve was performed with.
    CURLOPT_USERAGENT => $result['userAgent'],
    CURLOPT_POSTFIELDS => http_build_query([
        'cf-turnstile-response' => $result['code'],
    ]),
]);

$response = curl_exec($ch);
curl_close($ch);

Если токен отклоняется, а cData свежий, почти всегда причина именно в этом.

Прокси

$result = $solver->turnstile($sitekey, $pageUrl, [
    'data' => $cData,
    'pagedata' => $chlPageData,
    'proxy' => ['type' => 'HTTPS', 'uri' => 'user:[email protected]:3128'],
]);

Решайте через тот же исходящий канал, с которого будете отправлять, когда задание чувствительно к геолокации. Прокси работают для Turnstile, reCAPTCHA и GeeTest, но не для капч-картинок, которые никогда не касаются целевого сайта.

Ошибки

use CapSkip\Exceptions\ApiException;
use CapSkip\Exceptions\NetworkException;
use CapSkip\Exceptions\TimeoutException;

try {
    $result = $solver->turnstile($sitekey, $pageUrl, $options);
} catch (NetworkException $e) {
    // CapSkip is not running on the configured port
} catch (ApiException $e) {
    // often a stale cData, since it is bound to one page load
} catch (TimeoutException $e) {
    // exceeded recaptchaTimeout
}

Всё находится в CapSkip\Exceptions, в отличие от SDK для Python и Node, которые экспортируют исключения из корня пакета.

Часто задаваемые вопросы

Нужен ли виджетам когда-либо cData?

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

Мой токен действителен, но отклоняется.

Почти всегда дело в user agent. Установите CURLOPT_USERAGENT в $result['userAgent'] вместо того чтобы оставлять значение cURL по умолчанию. Следующая наиболее вероятная причина — устаревший cData, который сохраняется лишь при одной загрузке страницы.

Должно ли это выполняться внутри веб-запроса?

Желательно нет. PHP синхронен, а решение Turnstile занимает несколько секунд, поэтому выполнение его во время рендеринга страницы блокирует воркер. Перенесите это в задачу из очереди или в CLI-воркер.

Сводка

Виджетам нужны sitekey и URL страницы. Страницам с заданием требуются data и pagedata считывается заново со страницы, и токен должен быть отправлен вместе с $result['userAgent']. Импортируйте исключения из CapSkip\Exceptions, и не занимайте решениями потоки ваших запросов.

Другие языки — на Сервис распознавания Cloudflare Turnstile странице, детали параметров в Документация по API, и более широкий PHP-интерфейс на Решатель капчи для PHP странице. CapSkip является безлимитный сервис распознавания капчи работает на вашем компьютере.