简介:

在本文中,您将了解如何轻松绕过验证码和机器人程序保护或以其他方式无效。

我还没有看到关于这个主题的足够好的资源,特别是关于绕过 hCaptcha,所以我决定写这篇文章来展示验证码是多么无效。

该网站每月的页面浏览量超过 2 亿次,应用程序下载量超过 100 万次,您可以使用机器人进行的操纵和诈骗数量是疯狂的(您也需要代理),尤其是在像我这样拥有大量流量的网站上测试。

编写机器人

我去了公司的注册页面,它包含了通常的员工、电子邮件、密码、隐私政策,当然还有验证码。

signup.png

如您所见,验证码是来自一家名为“Intuition Machines Inc”的公司的 hCaptcha,它被大量网站用于“保护”免受机器人、DDoS 攻击等。

我要做的第一件事是使用 undetected-chromedriver;它是一个自定义的 selenium chromedriver,可以修补 chromedriver 的二进制文件。它也可以通过编辑二进制文件手动完成,但在我们的例子中是不必要的。

对于 hCaptcha,我们可以使用名为 2captcha 的服务;这是一种基于人的反验证码服务。该服务通过提供注册 url(例如example.com/signup)和一个站点密钥来工作,然后它将我们的请求发送到他们的系统,他们的一名员工解决了我们的验证码,最后我们得到了验证码令牌。

编写代码

进口:

import logging
import os
from random import randint
from time import sleep

import undetected_chromedriver as uc
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
from twocaptcha import TwoCaptcha

创建类并添加机器人的信用:

class Registration:
    def __init__(self):
        self._email_address = "example@gmail.com"
        self._password = "BotsForEver21!"

        self.driver = uc.Chrome()

在进行注册之前,我将添加一个新的实用程序方法,该方法将帮助我将电子邮件和密码插入相关表单:

def _insert_element(self, element, element_data, typing_speed=0.4):
    try:
        ActionChains(self.driver).move_to_element(element).click().perform()
        for c in element_data: 
            element.send_keys(c)
            sleep(typing_speed)
    except Exception as e:
        logger.exception(e)
  • typing_speed 用于模拟像用户一样的真实打字(每个字符 0.4 秒)。

添加实用程序方法后,我们就可以开始编写寄存器函数了;我们可以很容易地找到 email 和 password 元素,因为它们有自己的 id(user_email 和 user_password)。

def register_user(self):
    try:
        self.driver.get("https://www.somebigwebsite.com/signup")
        sleep(randint(5, 7))

        # insert email
        email_element = WebDriverWait(self.driver, 50).until(
            EC.presence_of_element_located((By.ID, "user_email"))
        )
        self._insert_element(
            element=email_element,
            element_data=self._email_address,
        )
        sleep(randint(4, 13))  # thinking about a password...

        # insert password
        password_element = WebDriverWait(self.driver, 50).until(
            EC.presence_of_element_located((By.ID, "user_password"))
        )
        self._insert_element(
            element=password_element,
            element_data=self._password,
        )
        sleep(randint(2, 5))  # agree or not?

        # agreements
        agree_element = WebDriverWait(self.driver, 30).until(
            EC.presence_of_element_located((By.ID, "tos_agreement"))
        )
        agree_element.click()
        sleep(randint(2, 3))
    except Exception as e:
        logger.exception(e)

好的,现在我们已经将机器人凭据插入到表单中,我们已经到了需要解决 hCaptcha 的地步。根据我的经验,解决 hCaptcha 总是比 reCAPTCHA 有点棘手和困难。

为了通过验证码,我们需要获取验证码令牌。

创建一个新函数来解决验证码:

def _solve_captcha(self):
    try:
        solver = TwoCaptcha(os.getenv("TWO_CAPTCHA_KEY"))
        captcha_token = solver.hcaptcha(
            sitekey=os.getenv("SITE_KEY"), url=self.driver.current_url
        )["code"]
        return captcha_token
    except Exception as e:
        logger.exception(e)

现在回到我们的 register_user 函数,我们可以使用 _solve_captcha() 函数,并注入验证码_token,但是我们应该在哪里注入验证码_token?

让我们打开开发工具并找出答案。在开发工具中搜索“h-captcha”会得到以下信息:

<textarea id="g-recaptcha-response-03i18g8r8ezr" name="g-recaptcha-response" style="display: none;"></textarea>
<textarea id="h-captcha-response-03i18g8r8ezr" name="h-captcha-response" style="display: none;"></textarea>

我将尝试通过将验证码_token 注入 h-captcha-response 元素来以“传统”方式进行操作,并希望这将被接受:

def _inject_captcha_token(self, captcha_token):
    try:
        self.driver.execute_script(
            """
            document.getElementsByName('h-captcha-response')[0].innerHTML = arguments[0]
            """,
            captcha_token,
        )
    except Exception as e:
        logger.exception(e)

在将验证码_token 注入到 h-captcha-response 后,它应该看起来像这样:

<textarea id="h-captcha-response-09xrd587ap08" name="h-captcha-response" style="display: none;">P0_eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.....</textarea>

即使在注入后,注册按钮也无法点击:

button.png

<button id="sign-up-button" disabled="disabled" data-target="captcha.submit sign-up.submit" type="submit" data-view-component="true" class="dark:focus:tw-outline-none dark:focus:tw-text-white dark:hover:tw-bg-primary-400 dark:hover:tw-text-white dark:tw-text-white disabled:tw-opacity-50 focus:tw-outline-none focus:tw-text-white hover:tw-bg-primary-700 hover:tw-text-white tw-bg-primary-500 tw-border tw-border-transparent tw-font-medium tw-inline-flex tw-items-center tw-leading-4 tw-rounded-md tw-text-white tw-justify-center tw-flex tw-w-full tw-py-3.5 tw-text-sm">
Sign up
</button>

让我们让它可点击:

signup_button_element = WebDriverWait(self.driver, 30).until(
    EC.presence_of_element_located((By.ID, "sign-up-button"))
)
# make the button clickable:
self.driver.execute_script(
    'arguments[0].removeAttribute("disabled");', signup_button_element
)

删除 disabled 属性后,按钮是可点击的,我们可以简单地做:

signup_button_element.click()

我们点击了注册底部,但不幸的是我们得到了“HCaptcha 验证失败”。错误 :(

failed.png

我们对于它可以做些什么呢?我们的验证码解决方案未被接受。

让我们退后一步,看看手动解决验证码后 html 元素会发生什么,然后我们将尝试将其转换为代码。经过一番研究,我发现了一个有趣的 iframe,它在解决验证码后会发生变化

<iframe src="https://newassets.hcaptcha.com/captcha/v1/5ddf2f3/static/hcaptcha.html#frame=checkbox&amp;id=0ni43p29wple&amp;host=www.findout.com&amp;sentry=true&amp;reportapi=https%3A%2F%2Faccounts.hcaptcha.com&amp;recaptchacompat=true&amp;custom=false&amp;tplinks=on&amp;sitekey=.....:)&amp;theme=light" title="widget containing checkbox for hCaptcha security challenge" tabindex="0" frameborder="0" scrolling="no" data-hcaptcha-widget-id="0ni43p29wple" data-hcaptcha-response="" style="width: 303px; height: 78px; overflow: hidden;"></iframe>

我尝试将验证码_token 注入“data-hcaptcha-response”,但效果不佳,我得到的只是验证再次失败。

这比我想象的要复杂。我又试了一次,我想看看为什么它失败了。在 HTML 中搜索和挖掘之后,我发现了一个新的隐藏元素,一旦成功解决了验证码,就会添加该元素。

<input type="hidden" name="response_token" value="P0_eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...">

现在我知道我所要做的就是将验证码_token 注入到响应_token 中,因此我创建了一个隐藏的输入元素并注入了验证码_token。

def _inject_captcha_token(self, captcha_token):
    try:
        js_script = f"""
            let captchaElement = document.getElementById('captcha')
            let inputElement = document.createElement('input');
            inputElement.setAttribute('type', 'hidden');
            inputElement.setAttribute('name', 'response_token');
            inputElement.setAttribute('value', "{captcha_token}");
            captchaElement.appendChild(inputElement)
        """
        self.driver.execute_script(js_script)
    except Exception as e:
        logger.exception(e)

我再次运行脚本,我们的机器人注册终于成功了 :)

成功.png

现在我们所要做的就是确认我们的帐户(当然这也可以很容易地自动化)。

结论

我们看到即使在更大、更“安全”的网站上绕过验证码等安全机制是多么容易。您也可以使用机器人在平台上执行自动化任务。

我希望你喜欢阅读这篇文章!

Logo

学AI,认准AI Studio!GPU算力,限时免费领,邀请好友解锁更多惊喜福利 >>>

更多推荐