Срок действия токена reCAPTCHA: как долго токены остаются валидными

Срок действия токена reCAPTCHA составляет две минуты. Документация Google говорит об этом прямо: каждый токен-ответ действителен две минуты и может быть проверен только один раз. Промахнётесь с любой из двух половин, и сервер вернёт вам одну и ту же бесполезную ошибку. В этой статье разбираются три отдельных таймера, которые принимают за один, куда на самом деле уходят эти 120 секунд в автоматизированном сценарии, и изменение порядка действий, которое устраняет почти любой баг с истёкшим токеном.
Две минуты и ровно одна проверка
У правила две половины, и обе кусаются.
Две минуты. Отсчёт начинается в момент выдачи токена, а не в момент отправки формы. Токен, который лежит в скрытом поле, пока пользователь дописывает текст, уже тратит свой бюджет.
Одна проверка. Отправка одного и того же токена в Google второй раз завершается ошибкой, даже через секунду. Так задумано: именно это не даёт переиграть перехваченный токен. Если ваш бэкенд проверяет токен один раз в middleware и ещё раз в обработчике, второй вызов падает, и баг выглядит плавающим.
Оба сбоя возвращают одно и то же. Ответ проверки от Google приходит с success, равным false, и кодом ошибки timeout-or-duplicate, который означает, что ответ либо слишком старый, либо уже был использован. Какой именно случай, он не сообщает, поэтому считайте это одним классом ошибок и проверяйте оба варианта.
Три таймера, а не один
Основная путаница здесь возникает из-за того, что три разных таймера сводят к одному понятию “срок действия токена”. Они разные и истекают независимо друг от друга.
| Таймер | Длительность | Что происходит, когда он истекает |
|---|---|---|
| Собственный ответ виджета, v2 checkbox | 2 минуты | Виджет очищает себя и вызывает expired callback. Скрытое поле становится пустым |
| Токен-ответ, на стороне сервера | 2 минуты | Проверка возвращает timeout-or-duplicate |
| Ваша сессия на целевом сайте | Зависит от сайта | Не связано с reCAPTCHA. Свежий токен не починит мёртвую сессию |
Первого таймера никто не ждёт, потому что он работает на стороне клиента и молча. На v2 checkbox виджет через две минуты сам аннулирует свой ответ и вызывает ту функцию, которую вы зарегистрировали как expired callback. Если вы не зарегистрировали ничего, отмеченный флажок так и остаётся отмеченным на экране, хотя скрытое поле за ним пустое, поэтому форма уходит вообще без токена, и сервер сообщает об ошибке отсутствующего ввода, а не об истёкшем сроке.
<!-- Register the callback. Without it the box looks ticked
while the value behind it is already gone. -->
<div class="g-recaptcha"
data-sitekey="YOUR_SITEKEY"
data-callback="onSolved"
data-expired-callback="onExpired"></div>
<script>
function onExpired() {
// Reset the widget and re-enable whatever you disabled.
grecaptcha.reset();
}
</script>Сколько живут другие проверки?
Правило двух минут работает не везде. Если вы обрабатываете больше одного типа проверок, разница в бюджетах уже имеет значение.
| Challenge | Срок действия токена | Повторное использование |
|---|---|---|
| reCAPTCHA v2, checkbox и Invisible | 2 минуты | Нет |
| reCAPTCHA v3 | 2 минуты | Нет |
| reCAPTCHA Enterprise | 2 минуты | Нет |
| Cloudflare Turnstile | 5 минут | Нет |
| GeeTest v3 | Отправляйте его обратно сразу | Нет |
Turnstile здесь самый щедрый. Руководство Cloudflare по серверной валидации даёт токену 300 секунд и отклоняет повторно отправленный с тем же кодом timeout-or-duplicate, который использует Google. GeeTest v3 работает наоборот: значение challenge, которое вы передаёте на решение, одноразовое и истекает примерно за минуту, поэтому дедлайн наступает до решения, а не после него. Получайте его непосредственно перед решением, а не в начале скрипта.
Куда на самом деле уходят две минуты
При ручном заполнении формы бюджет огромен. В автоматизированном сценарии он теснее, чем кажется, потому что решение не мгновенное.
| Шаг | Типичное время |
|---|---|
| Загрузить страницу и прочитать sitekey | от 1 до 3 секунд |
| Решить reCAPTCHA v2 | от 15 до 20 секунд |
| Решить reCAPTCHA v3 | От 10 до 15 секунд |
| Вставить токен и отправить | меньше секунды |
| Остаётся | примерно 95 секунд |
Запас в девяносто пять секунд выглядит комфортно, пока в середину не влезает что-то ещё. Обычные виновники: рукопожатие прокси, шаг входа, вставленный между решением и отправкой, ограничитель частоты, который усыпляет воркер, или очередь, которая копит решённые токены для последующего использования. Последнее не работает никогда: токен быстро портится и не является ресурсом, который можно складывать в пул.
Берите токен последним
Почти любой баг с истёкшим токеном лечится порядком действий. Сначала делайте всё медленное, а токен берите последним шагом перед запросом, который его расходует.
# pip install capskip
from capskip import CapSkip
solver = CapSkip(host="127.0.0.1", port=8080)
# Slow things first: log in, warm the session, pick up cookies.
session = build_session()
sitekey = read_sitekey(session, PAGE_URL)
# Then solve, so the clock starts as late as possible.
result = solver.recaptcha(sitekey=sitekey, url=PAGE_URL)
# And submit straight away. Nothing goes between these two lines.
session.post(PAGE_URL, data={"g-recaptcha-response": result["code"]})Из этого следуют два правила, и они покрывают почти всё остальное.
- Никогда не кэшируйте токен. Ни в Redis, ни в переменной, которая живёт дольше запроса, ни между повторами. Вместо этого решайте заново
- Никогда не проверяйте дважды. Проверяйте ровно в одном месте. Если middleware уже проверил токен, обработчик маршрута должен прочитать этот результат, а не вызывать Google снова
Повторы заслуживают отдельного слова. Если отправка не удалась и вы её повторяете, уже отправленный токен израсходован, поэтому повтору нужно новое решение. Повторять всю единицу работы правильно. Повторять только HTTP-вызов со старым токеном означает получать timeout-or-duplicate каждый раз, и выглядит это так, будто решатель возвращает плохие ответы.
Задержки и место запуска решателя
Поскольку CapSkip работает на вашем собственном железе, ни одна секунда двухминутного бюджета не уходит на обращение к стороннему эндпоинту через интернет. Local mode слушает 127.0.0.1, и вызов вообще не покидает машину.
Server mode меняет это немного, и стоит знать, насколько. Если направить воркеры на общий решатель в вашей сети или на VPS с публичным IP, добавляется один сетевой хоп на вызов: миллисекунды в локальной сети и десятки миллисекунд до VPS в том же регионе. На фоне 120 секунд это шум, зато вы получаете один решатель, обслуживающий весь флот. Оба режима настраиваются в разделе Настройки подключения, а для серверного варианта рекомендуется статический публичный IP.
Сюда же относится одна деталь CapSkip: решённый результат читается один раз. Повторный опрос того же идентификатора задачи не вернёт ответ снова, поэтому сохраняйте токен при первом чтении, а не запрашивайте его позже заново.
FAQ
Можно ли продлить эти две минуты?
Нет. Окно задаёт Google, и нет ни настройки сайта, ни параметра, ни тарифа, которые бы это меняли. Единственный доступный рычаг: делать меньше между решением и отправкой.
Истекает ли токен v3 быстрее из-за оценки?
Нет. Токены v3 получают те же две минуты, что и v2. Оценка относится совсем к другому: она описывает, как выглядел трафик, а не сколько живёт ответ, и она не снижается, пока токен лежит без дела. Учтите, что в CapSkip нет опции минимальной оценки, поэтому на стороне решения с ней тоже ничего не связано.
Локально мой токен работает, а в продакшене истекает. Почему?
Почти всегда виновата очередь. Локальные запуски идут прямо от решения к отправке, а в продакшене задача сначала проходит через брокер, пул воркеров или ограничитель частоты. Измерьте разрыв между двумя отметками времени в продакшене, и обычно окажется, что он больше 120 секунд. Перенесите решение в тот воркер, который выполняет отправку.
Бывает ли timeout-or-duplicate виной самого сайта?
Иногда. Страница, которая сама проверяет токен перед тем, как переслать ваш запрос, израсходует его, и ваша собственная проверка после этого падает как дубликат. Прокси-слои и веб-файрволы приложений иногда делают то же самое. Если токен падает при первом использовании со свежей отметкой времени, подозревайте, что его потратил кто-то выше по цепочке.
Коротко
Две минуты, одна проверка и три отдельных таймера, которые принимают за один. Зарегистрируйте expired callback, чтобы браузерный случай был виден, решайте как можно позже и никогда не кэшируйте и не переигрывайте токен. Поскольку решать заново ничего не стоит, когда у вас есть локальный сервис распознавания капчи, повторное решение всегда будет правильным ответом на истёкший токен. Механику каждой версии смотрите в руководстве по reCAPTCHA v2 и руководстве по v3, странице Turnstile для случая с пятью минутами, а также в материале о том, что такое reCAPTCHA для общего контекста.
