【Puppeteer実用講座】Puppeteer操作だけでreCAPTCHA v2は突破したときの話〜マウスクリック編


2022/05/05

Google reCAPTCHA v2は「私はロボットではありません」でお馴染みの現在広く認知された認証方式の一つになっています。

今回はPuppeteerでreCAPTCHA v2認証をパスするような方法を簡単に検証します。


reCAPTCHA v2のあるログイン認証

reCAPTCHA v2を採用している某ネット証券でログインできるかを確認します。

合同会社タコスキングダム|蛸壺の技術ブログ

reCAPTCHAは埋め込みのiframeによって提供される仕組みです。

埋め込みのiframe要素は、puppeteerで別のブラウザコンテクストとして扱われますので、通常の要素セレクタからクリックでログイン試みるやり方だと、クリックは容易ではありません。

iframe要素を考慮した対策でreCAPTCHAを突破することが必要になります。


クリックをマウス型で試してみる

reCAPTCHA v2の仕組みはiframe要素をブラウザ閲覧者が直接マウスクリックすることで初めて動作するJavascript駆動の認証方式です。

ポイントとしてはreCAPTCHAが描画されるiframeの直前の要素か、もしくは一層上の親ノードをターゲットの座標を取得することにあります。

合同会社タコスキングダム|蛸壺の技術ブログ

基準となる位置が取得できたら、そこから少し下目の座標にカーソルを移動、そしてiframeが描画されている領域をクリックすることで、擬似的にマウスクリックが実現できます。

ということで、今回のウェブサイトのログインに合わせた実装は以下のようになります。

            
            import puppeteer from 'puppeteer';
const browser: puppeteer.Browser = await puppeteer.launch({headless: true});
const page: puppeteer.Page = await browser.newPage();

try {
    //👇reCAPTCHA式のログインページに移動
    await page.goto('https://某D●M証券/login');

    //👇ユーザー名&パスワードを打ち込む先のフォーム要素
    const form = await page.$("form.c-form");
    if (form) {
        //👇各inputにユーザー名とパスワードを入力
        const user = await form.$("input[name='email']");
        await user?.type('ユーザーID・Email');
        const password = await form.$("input[name='password']");
        await password?.type('パスワード');
        await page.waitForTimeout(500);

        //👇reCAPTCHAのiframeをラップしている親要素を選択
        const targetElementSelector = "div.g-recaptcha";
        await page.waitForSelector(targetElementSelector);

        //👇先程の親要素のHTMLページ中の座標系情報を取得
        const recaptchaPosition = await page.evaluate((selector) => {
            const el = document.querySelector(selector);
            const rect = el.getBoundingClientRect();
            return {
                height: rect.height,
                width: rect.width,
                x: rect.left,
                y: rect.top
            };
        }, targetElementSelector);

        //👇先程取得した座標からiframeが描画されている分少しずらした位置でマウスクリック!
        const margin = 3;
        if (recaptchaPosition) {
            await page.mouse.move(recaptchaPosition.x + margin, recaptchaPosition.y + margin, { steps: 1 });
            await page.mouse.click(recaptchaPosition.x + margin, recaptchaPosition.y + margin);
            await page.waitForTimeout(1000);
        }

        //👇入力操作が終わったらフォーム送信
        const submit = await form.$("button[type='submit']");
        submit?.focus();
        submit?.click();
    }
}
catch (error) { console.log(error); }
finally { browser.close(); }
        

上の方法でマウスクリックをエミュレートするやり方でならばreCAPTCHAのiframeを上手くクリックすることが可能です。

運良く、画像クイズがでなければそのままログインも可能ですが、最初にログインしてくるような素性のわからないブラウザ環境からだと必ずと言っていい確率で例のアレが出てきます。

合同会社タコスキングダム|蛸壺の技術ブログ

まぁ結局の所、ヘッドレスのままで画像クリック照合がクリアするのが難しいので、一度ログインしてクッキーが残っているブラウザからヘッドレスで使うと上手くいくかも知れませんが...運次第ですかね。

画像クイズによる認証はさすがにヘッドレスブラウザ環境だと厳しいので、今後は別のログイン手段を模索します。


参考サイト

Google reCAPTCHA v2はどうするとスパム判定されるのか?

記事を書いた人

記事の担当:taconocat

ナンデモ系エンジニア

主にAngularでフロントエンド開発することが多いです。 開発環境はLinuxメインで進めているので、シェルコマンドも多用しております。 コツコツとプログラミングするのが好きな人間です。