Как решить Cloudflare Turnstile в PHP с помощью 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 submitUser 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 является безлимитный сервис распознавания капчи работает на вашем компьютере.
