ReCaptcha (она же всенародно любимая «капча») — одна из самых болезненных вещей, с которой может столкнуться автоматизатор тестирования на своём пути. В Сети гуляют тысячи разнообразных видео, записанных выходцами из солнечной Индии, касательно того, какими танцами с бубном возможно обмануть этого зверя. Действительно, достаточно сложно пытаться взаимодействовать с помощью запрограммированных скриптов со штукой, основная цель которой — убедиться что «вы не робот».

Очень важный дисклеймер: обмануть капчу невозможно.

Если вы уже столкнулись с этой проблемой, и читаете эту статью, пытаясь нагуглить рецепт панацеи, то знайте, что его не существует. Тем более, в вашей голове уже скорее всего возникли инновационные мысли о том, чтобы сымитировать реалистичное поведение пользователя с помощью WebDriver, путём рандомного mouse overing'а элементов, кликов по инпутам, и бережно расставленных Thread.sleep(). Абсолютно точно известно, что этот подход работать не будет, не тратьте свое время попусту.

image

Получается, выхода нет?

Не все так пессимистично. Иногда достаточно постараться дать себе наиболее точный ответ на вопрос «Какая задача передо мной стоит?» и посмотреть на ситуацию шире. В большинстве случаев, вы поймете, что ваша цель не обмануть капчу, а обойти её, чтобы протестировать функционал, спрятанный за ней. На примере своего кейса, я поделюсь с вами найденными мною вариантами решения поставленной задачи.

Контекст: мы интегрировали часть своего продукта внутрь стороннего сервиса, и хотели мониторить, все ли в порядке на их стороне, т.к. они не занимаются покрытием third-party частей своей платформы. Чтобы получить доступ к нашему функционалу, сперва необходимо было залогиниться. Тут-то я и встретился с капчей лицом к лицу. Далее привожу все перепробованные мною варианты обхода данной проблемы.


Нерабочие


Залогиниться через Google или Facebook


Помимо классической аутентификации, присутствовали каноничные «Login with Google / Facebook». Само собой, там точно также присутствовали свои «капчи», поэтому этот вариант не помог решить проблему.

Имитация поведения пользователя


Да, я тоже это пробовал. Было забавно, но чересчур наивно.



Рабочие


Chrome / Firefox Profiles


Поговорим о первом «живом» варианте. В драйверах для этих браузеров (chromedriver / geckodriver) реализована возможность загружаться под заранее заготовленным User Profile. Он хранит в себе все сохраненные пароли, куки, сессии, и даже историю браузера и закладки. Т.е. таким образом мы попросту пропускали абсолютно неважный для нашей задачи шаг логина, и таким образом попадали сразу на страницу с объектом тестирования. Реализуется это следующим образом:

  1. Создаем «чистый» профиль браузера
  2. Вручную вводим капчу и логинимся на нужный ресурс
  3. Копируем необходимый профиль в наш проект (HOWTO для Firefox и Chrome)

После чего, нам необходимо сказать драйверу, что грузиться он должен именно с указанного профиля:

Firefox:

// Инициализируем профиль
FirefoxProfile profile = new FirefoxProfile(new File("/путь/к/вашему/профилю"));
// Указываем профиль в передаваемых опциях
FirefoxOptions options = new FirefoxOptions();
options.setProfile(profile);
// Создаем браузер
WebDriver driver = new FirefoxDriver(options);

Chrome:

// Указываем профиль в передаваемых опциях
ChromeOptions options = new ChromeOptions();
options.addArgument("--user-data-dir=/путь/к/папке/с/профилями");
options.addArgument("--profile-directory=Название_папки_с_нужным_профилем");
// Создаем браузер
WebDriver driver = new ChromeDriver(options);

Этот подход хорошо показал себя при тестировании на локальной машине с установленным браузером и обычными gecko-/cromedriver’ами, но возникли проблемы при запуске на Jenkins. Мы поднимаем Selenium хаб и ноды внутри Kubernetes кластера, поэтому мы столкнулись с неприятностями в виде слишком долгого по времени маунта директории внутрь контейнера (чистый профиль в среднем весит около 25 MB, что немало), а так же некоторых проблем с CRUD правами браузера, который не мог вносить изменения в профайл в рантайме, и падал с “unknown error: failed to write prefs file” эксепшеном. Ко всему прочему, апдейтить профайл после достижения куками и сессиями своих Expiration Dates достаточно неудобно, да и не хотелось держать в проекте огромную папку с внутренностями профиля, поэтому в конечном итоге окончательным был выбран следующий вариант.

Cookies


“А ларчик просто открывался” — именно так можно было охарактеризовать ситуацию, после того, как мы просто добавили полученные вручную куки в драйвер. Алгоритм действий максимально прост и не зависит от выбранного браузера:

  1. Логинимся вручную
  2. Через Network смотрим Request Headers -> Cookie которые посылает наш браузер

Добавляем их в наши тесты следующим образом:

// Создаем cookie
private static final Cookie COOKIE = new Cookie("имя", "содержимое", "домен", "путь", new Date("дата"));
// Создаем браузер
WebDriver driver = new ChromeDriver(options);
// Добавляем cookie в браузер
driver.manage().addCookie(COOKIE);

Очевидный минус этого подхода — необходимость вручную менять куки после истечения их срока валидности. Но, в виду того что на тестируемой платформе этот срок составляет 3 месяца — мы и выбрали это решение.



А если мне не нужно логиниться?



А как же ситуация, когда речь идет не о авторизации и сессиях, а о совершении какого-либо одноразового действия (e.g. оформление заказа из корзины, регистрация нового пользователя и т.п.)? Здесь ситуация еще хуже. Два варианта которые я смог обнаружить, это:

  1. Договориться с вашими разработчиками о предоставлении вам некого workaround'а. Google предоставляет такую возможность, но помните, что вы осознанно делаете небольшую дыру в security.
  2. Воспользоваться сторонними платными сервисами, которые принимают с вашей стороны скриншот капчи, пытаются его декодировать, и отправляют вам расшифрованное значение. Сам я такой способ не пробовал и полностью рекомендовать его не могу.



Подведем итоги


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

Спасибо за внимание.

P.S. Если вы знаете еще какие-либо работающие в реальной жизни решения — пожалуйста, опишите их в комментариях, будет очень интересно почитать.

Комментарии (18)


  1. Noiwex
    28.02.2019 09:34

    Есть сервисы, которые за вас решают рекапчу. Вы им site secret и challenge id, а они вам спустя какое-то время код ответа.


    1. Mnemonik
      28.02.2019 10:16

      Если вы про то как просто решить ими проблему автоматического тестирования, то они платные. Так себе идея если это для тестов, как в статье.
      Если вы про то как легко и просто обойти капчу, вон какой есть классный способ и капча не работает, то вполне достаточно реализовать капчу так, как рекомендует гугл, проверяя в его ответе не только success/failure, но и другие параметры которые он возвращает.
      Капча — работает. Сервисы эти эффективны только против небрежной реализации.


      1. Akuma
        28.02.2019 12:00

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


        1. Kovylin77 Автор
          28.02.2019 12:48

          Зависит от количества ваших тестов и частоты их прогона. Используя самый популярный, на выходе получается дешево $1.39 за 1000 решенных капч. Плюс, к тому же все они дают цифру в ~90% вероятности успеха решения. Т.е. каждый десятый тест потенциально будет flaky, что не есть хорошо


          1. Akuma
            28.02.2019 12:57

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

            На своем примере за февраль: $28 за 36000 каптч. Там не только рекаптча, но и ставка у меня стоит максимально высокая, чтобы повысить скорость.

            Это около 1000 тестов в сутки. Если у вас правда столько всего, вы не заметите расходов в 30 баксов.


            1. Kovylin77 Автор
              28.02.2019 12:58

              Хм, звучит действительно неплохо, спасибо
              А какой процент неверно решенных капч?


              1. Akuma
                28.02.2019 13:11

                Не знаю где посмотреть именно процент.
                На основании данных сервиса (Анти-каптча, если что) около 250 ошибок (вроде бы за все время), но по ощущениям их больше.

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

                Ну и «втупую» рекаптча может решаться по две минуты. Если вам нужно быстрее, там есть способ это ускорить.

                Дополнию, что только рекапча будет стоить дороже, чем у меня. Но, блин, зачем вам столько тестов с ней гонять? :)


  1. dididididi
    28.02.2019 10:03

    Не сталкивался с капчой ни разу, а что отключить ее на тестовой среде так сложно?


    1. Kovylin77 Автор
      28.02.2019 12:43

      Думаю не сложно. Но тут играли роль два фактора:
      — Это был сторонний сервис, на который мы не имеем большого влияния
      — Это был production environment, на котором ее точно отключать не нужно


  1. tuxi
    28.02.2019 10:15

    А на Яндекс.Поиске не пробовали тестировать? Я имею в виду ситуацию, когда на каком-нибудь очередном запросе вылезает капча «нам показалось тут что вы...»


  1. Teomit
    28.02.2019 11:34

    Очень важный дисклеймер: обмануть капчу невозможно.

    Ну я не был бы так категоричен:
    Статья 1
    Статья 2


    1. Kovylin77 Автор
      28.02.2019 12:44

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


  1. savkk
    01.03.2019 11:48

    Честно, так и не понял о чем статья. О там как пользоваться куками в Selenium? Ну не знаю…
    Если есть возможно, то лучше мониторить изменения на стороннем сервисе через предоставляемое ими API, может есть такая возможность?
    Вообще не понимаю, зачем заходить туда Selenium'ом, таким образом подвергать опасности и без того хрупкие UI-тесты.
    В случае необходимости периодически руками проверять UI.
    Да и вообще тестить сторонние сервисы, да еще и на проде, это мне кажется прям совсем не правильно.


    1. Kovylin77 Автор
      01.03.2019 12:52

      — Открытого API нет
      — Немного не понял, каким образом мы «подвергаем опасности хрупкие UI-тесты»? :)
      — Мы тестим не сторонний сервис, а наше приложение, интегрированное в него. Именно поэтому мы позволили себе обойти шаг логина, т.к. это как раз тестирование стороннего сервиса, в котором мы не заинтересованы


      1. savkk
        01.03.2019 13:18

        К вашу функционалу вы попадаете без участия функций сторонней системы?


        1. Kovylin77 Автор
          01.03.2019 14:05

          Минимальное участие само собой присутствует. Но убрав логин, мы минимизировали количество шагов, в которых мы не заинтересованы


  1. polarnik
    03.03.2019 10:05

    Здравствуйте, Михаил. Хорошая тема.
    В тестировании производительности использую более простые на мой взгляд способы.

    Шаг 1. I'd like to run automated tests with reCAPTCHA. What should I do?

    Site key: 6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI
    Secret key: 6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe
    Эти настройки указываются в конфигурационном файле сервера приложений.

    Шаг 2. Настройка Fiddler и работы приложений через прокси-сервер. Так как сервер приложений всё равно будет отправлять запросы в Google, а Google для такой отладочной связки Site key + Secret key будет всегда говорить — ok. Можно автоматизировать процесс, сделать так чтобы сервер приложений работал с Google через прокси-сервер. А в прокси-сервере (использую Fidder) настроить автоответ на запрос.

    2.1. Сохранить оригинальный ответ от Google ReCapcha.
    2.2. Используя auto response в Fiddler сделать так, чтобы на все запросы к Google ReCapcha был фиксированный положительный ответ.

    В ответе будет сказано, что результат проверки — ok. Но будет сказано, что время ответа было в прошлом. Если сервер приложений валидирует не только ответ true/false, но и время. То автоответ не сработает. Но, обычно, сервер приложений время не валидирует, что позволяет сделать замкнутую систему.

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


    1. Kovylin77 Автор
      03.03.2019 14:59

      Очень крутое решение!
      Спасибо, обязательно попробую, если предоставится такая возможность :)