Примеры кода написаны на React, TypeScript и Node.js.

Даже в 2022 году, спустя более десяти лет с момента изобретения reCAPTCHA, я считаю, что понять, как Google reCAPTCHA работает и интегрируется с веб-приложением, по-прежнему не так просто, если вы впервые смотрите на него, благодаря минимальному количеству официальных документов.

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

Вот почему я написал эту статью — чтобы сделать шаг вперед для тех, кто хочет понять Google reCAPTCHA проще и быстрее.

Основы

Чтобы обсудить Google reCAPTCHA, нам нужно начать с более простой концепции — CAPTCHA.

CAPTCHAрасшифровывается как «Полностью автоматизированный публичный тест Тьюринга для определения различий между компьютерами и людьми» [1], который является широко используемым методом предотвращения ботов. от выполнения таких действий, как регистрация учетной записи.

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

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

Таким образом, для борьбы с ботами вводятся более продвинутые методы тестирования, а наиболее популярным решением является Google reCAPTCHA.

Разбираться в разных версиях Google reCAPTCHA

Вступив в мир Google reCAPTCHA, вы сразу же столкнетесь с проблемой выбора из дерева доступных вариантов [2]:

  • reCAPTCHA v2 (флажок «Я не робот»)
  • reCAPTCHA v2 (невидимый значок reCAPTCHA)
  • reCAPTCHA v3

К сожалению, официальные документы имеют только краткое введение, оставляя радость глубокого погружения нам, желающим узнать больше.

reCAPTCHA v2 (флажок «Я не робот»)

Прежде всего, давайте предположим, что у нас есть такой экран регистрации:

Мы ожидаем, что поток будет:

«Вызов выбора изображения» — это всплывающее окно, как показано ниже:

Ключом к пониманию того, «как» и «почему» работает поток, является токен за кулисами:

  • Когда вы нажимаете «Я не бот», reCaptcha проверяет, не являетесь ли вы ботом.
  • Если reCAPTCHA посчитает, что вы вряд ли бот, то выдаст вам токен.
  • Если reCAPTCHA решит, что вы можете быть ботом, появится всплывающее окно с викториной по выбору изображения, и вы сможете получить токен только после того, как успешно ответите на викторину.
  • Когда веб-интерфейс отправляет окончательный запрос «регистрации» на сервер API, он не только содержит «имя пользователя» в полезной нагрузке, но также включает «токен», выданный reCAPTCHA.
  • Когда сервер API получает запрос, он проверит токен, обратившись к серверу reCAPTCHA, и предотвратит регистрацию пользователя, если токен недействителен.

Таким образом, мы можем представить поток с помощью следующих диаграмм последовательности:

При отсутствии вызова изображения (источник диаграммы):

Когда есть вызов изображения (источник диаграммы):

Пример кода: интерфейс

Давайте посмотрим на пример компонента React для деталей реализации.

Прежде всего, компонент отобразит форму с вводом имени пользователя, флажком reCAPTCHA, кнопкой регистрации и сообщением об ошибке.

<form className="Form" data-testid="FormWithCheckbox" data-registration-result={registrationResult}>
  <div>
    <label>
      <span>username: </span>
      <input type="text" name="username" value={username} onChange={handleUsernameChange} />
    </label>
  </div>

  <ReCAPTCHA sitekey={SITE_KEY} onChange={handleRecaptchaChange} />

  <button type="button" onClick={handleClickRegister}>
    register
  </button>

  {errorMessage && <div className="Error">{errorMessage}</div>}
</form>

Здесь стоит упомянуть, что мы используем библиотеку — react-google-recaptcha для удобной настройки и рендеринга reCAPTCHA.

Затем мы сохраняем состояния форм для имени пользователя и токена и обновляем их с помощью обратных вызовов (используемых в JSX).

const [username, setUsername] = React.useState("");
const [recaptchaToken, setRecaptchaToken] = React.useState<string | null>(null);

const handleRecaptchaChange = React.useCallback((value: string | null) => {
  console.log("FormWithCheckbox::handleRecaptchaChange > value: ", value);
  setRecaptchaToken(value);
}, []);

const handleUsernameChange = React.useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
  const value = trim(event.target.value);
  setUsername(value);
}, []);

Наконец, когда пользователь нажимает «зарегистрироваться», мы отправляем запрос API с именем пользователя и токеном.

const [registrationResult, setRegistrationResult] = React.useState("");
const [errorMessage, setErrorMessage] = React.useState("");

const handleClickRegister = React.useCallback(async () => {
  setErrorMessage("");

  if (!recaptchaToken) {
    alert("Please click reCAPTCHA checkbox!");
    return;
  }

  try {
    const response = await axios.post("/api/user/registration", {
      username,
      recaptchaToken,
      recaptchaVersion: "V2_CHECKBOX",
    });
    const { result } = response.data;
    console.log("FormWithCheckbox::handleClickRegister > result: ", result);
    setRegistrationResult(trim(result));
    alert("Register success");
  } catch (e: any) {
    const response = e.response;
    const { result, error } = response.data;
    setRegistrationResult(trim(result));
    setErrorMessage(trim(error));
  }
}, [recaptchaToken, username]);

Пример кода: бэкенд

Код node.js для обработки запроса API очень прост:

  • Подтвердить ввод пользователя
  • Проверить токен reCAPTCHA
  • Выполнить основное действие (например, зарегистрироваться)
  • Возвращаемый результат
app.post("/api/user/registration", async (req, res) => {
  const recaptchaVersion = _.trim(_.get(req.body, "recaptchaVersion"));
  const username = _.trim(_.get(req.body, "username"));
  const token = _.trim(_.get(req.body, "recaptchaToken"));

  try {
    validateUsername(username);
    const secret = getSecretByRecaptchaVersion(recaptchaVersion);
    await verifyRecaptchaToken({ token, secret });
    // ... Register ...
    res.status(201).json({ result: "SUCCESS", username });
  } catch (e) {
    console.error(e);
    res.status(400).json({
      result: "FAIL",
      error: _.get(e, "message") ? _.get(e, "message") : JSON.stringify(e),
    });
  }
});

…и вот код для проверки токена reCAPTCHA:

type VerifyRecaptchaTokenArgs = {
  secret: string;
  token: string;
};
const verifyRecaptchaToken = async (args: VerifyRecaptchaTokenArgs) => {
  const { secret, token } = args;
  if (!token || !secret) {
    throw new Error("reCAPTCHA secret or token is invalid");
  }

  const response = await axios.post(
    "https://www.google.com/recaptcha/api/siteverify",
    undefined,
    {
      params: { secret, response: token },
    }
  );

  console.log("reCAPTCHA siteverify result: ", response.data);

  if (!response.data.success) {
    throw new Error("reCAPTCHA token is invalid");
  }
};

Полный код можно найти на Github:



reCAPTCHA v2 (невидимый значок reCAPTCHA)

При использовании этой опции весь процесс идентичен варианту с флажком, за исключением того, что пользователю не нужно будет отмечать «Я не бот» — он неявно ставится, когда пользователь нажимает кнопку основного действия (например, «зарегистрироваться»). кнопка).

Таким образом, когда нет задачи изображения, диаграмма последовательности может быть построена, как показано ниже (источник диаграммы):

…и это при вызове выбора изображения (источник диаграммы):

Пример кода

Хотя внутренний код в основном идентичен предыдущему примеру с флажком, внешний интерфейс немного отличается: мы запускаем проверку reCAPTCHA, когда пользователь нажимает «зарегистрироваться», а не нажимает на флажок.

reCAPTCHA v3

Этот вариант сильно отличается от двух предыдущих — он не использует всплывающее окно «выбор изображения», чтобы проверить, является ли пользователь ботом или нет.

Вероятно, мы могли бы даже сказать, что CAPTCHA вообще не существует: reCAPTCHA v3 утверждает, что имеет возможность наблюдать за взаимодействием пользователей с сайтом и вычисляет оценку за действие пользователя, тогда наш сервер может использовать настраиваемый порог, чтобы решить, действие должно быть разрешено или заблокировано.

С диаграммой (исходником) было бы проще разобраться:

Очень важно понимать, что при использовании reCAPTCHA v3 у пользователя нет такой возможности доказать, что он/она не является ботом. Когда API получает результат проверки токена reCAPTCHA, мы можем либо разрешить пользователю продолжить, либо дать пользователю жесткую остановку с ошибкой на основе «оценки».

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

Пример кода

Примечание: мы используем библиотеку под названием react-google-recaptcha-v3, чтобы упростить интеграцию reCAPTCHA в приложение React.

Прежде всего, нам нужно инициализировать reCAPTCHA как можно раньше:

root.render(
  <React.StrictMode>
    <GoogleReCaptchaProvider reCaptchaKey={SITE_KEY}>
      <App />
    </GoogleReCaptchaProvider>
  </React.StrictMode>
);

Затем нам нужно получить токен и отправить запрос, когда пользователь нажмет «зарегистрироваться»:

const App = () => {
  const { executeRecaptcha } = useGoogleReCaptcha();

  const handleClickRegister = React.useCallback(async () => {
    if (!executeRecaptcha) {
      console.log("Execute recaptcha not yet available");
      return;
    }

    const recaptchaToken = await executeRecaptcha("register");

    // TODO: send API request and get result
  }, [executeRecaptcha, username]);

  return (
    <div className="App">
      {/* ... */}
      <button type="button" onClick={handleClickRegister}>
        register
      </button>
      {/* ... */}
    </div>
  );
};

Возможно, вы уже заметили, что при получении токена мы также сообщаем reCAPTCHA action, что означает «зарегистрироваться».

Наконец, нам нужно проверить токен с помощью «action» и «score» в бэкенде:

const verifyRecaptchaToken = async (args: VerifyRecaptchaTokenArgs) => {
  // ... Get token verify result ...

  const score = _.toNumber(_.get(response.data, "score"));
  const action = _.toString(_.get(response.data, "action"));

  if (action === "register" && score < 0.5) {
    throw new Error("Bot detected!");
  }
};

Как показано в приведенном выше коде, мы используем 0.5 в качестве порога для действия «регистрация». То есть, если оценка равна или выше 0,5, мы разрешаем пользователю зарегистрироваться; если оценка ниже 0,5, мы выдаем ошибку и останавливаем регистрацию пользователя.

Материал

Вы можете найти полные и работоспособные примеры кода для всех трех вариантов (флажок V2, невидимый V2 и V3) в репозитории на моем github:



Примечание: ключи для reCAPTCHA включены и жестко запрограммированы в репозитории для удобства. Я бы порекомендовал зарегистрировать собственную учетную запись и получить собственные ключи, если вы хотите узнать больше о reCAPTCHA.

Дальнейшее обсуждение

Хотя reCAPTCHA является наиболее популярным выбором, у вас могут быть некоторые опасения по поводу обязательства конфиденциальности, и эти опасения абсолютно обоснованы. Например, вы можете прочитать эту статью[3], чтобы понять, как привести ваше приложение в соответствие с GDPR при использовании reCAPTCHA.

В качестве альтернативы hCaptcha претендует на то, чтобы заменить Google reCAPTCHA, но более дружелюбен к различным политикам конфиденциальности. Так что, возможно, стоит взглянуть, если вас беспокоят политика и правила конфиденциальности.

Рекомендации

Больше контента на PlainEnglish.io.

Подпишитесь на нашу бесплатную еженедельную рассылку новостей. Подпишитесь на нас в Twitter, LinkedIn, YouTube и Discord.

Хотите масштабировать свой технический стартап с помощью контента? Посмотрите Цирк.

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