💬

JSとPuppeteerで「slider CAPTCHA」をバイパスする方法

2022/10/11に公開約9,100字

次のトピックは「slider CAPTCHA」をJSで解決する方法です

スパムはウェブサイトの所有者にとって大きな問題です。その一方でCAPTCHAは私をひどくイライラさせるもので、ユーザーエクスペリエンスに悪影響を及ぼします。

CAPTCHAは最悪です。素直に認めましょう。人間かどうか検証する方法はたくさんありますが、どの方法も最悪です。

近年、BOTは日々賢くなっており、BOTからウェブサイトを守ることは困難です。ですが多くの空き時間と十分なリソースがあれば、ほぼどんなCAPTCHAでもバイパスできます。

PuppeteerがreCAPTCHAを突破するためのプラグインがあります。 CAPTCHA突破サービスを提供している会社が存在します。たとえば2Captchaがあります。 Puppeteerと2Captchaの使い方はこちらです。

代替手段として「スライドして確認する」CAPTCHA を実装した ウェブサイトがあります。しかし、なぜ単純なスライダーCAPTCHAを使う人がいるのでしょうか?

1_0LXnPGyW3gHt_tKZlGL4Bg.png

理由は次のとおりです。

  • ほとんどのBOTはJSを実行しないため、BOTを止めることができる
  • スライダーはユーザーフレンドリー
  • スライダーにはモバイルユーザー向けの自然なスワイプアクションがある

したがって、スライダーは人間には使いやすくシンプルなものです。しかし、もっとスマートなBOTで簡単に解決することもできます。

いくつかのスライダーCAPTCHAをバイパスしてみましょう。

スライドして送信

「スライドして送信」フォーム用のjQueryプラグインです。これはフォーム上でスパムを防止するためのCAPTCHAの代替手段です。

まず、入力欄にデータを入力します。このスライダーを動かすには次のことを行う必要があります。

  • ハンドルの中心にマウスを置く
  • マウスをクリックする
  • マウスを動かす
  • マウスを離す

完了。こんなに簡単なのです。

const puppeteer = require('puppeteer' )

async function run () {
const browser = await puppeteer. launch({
      headless: false,
     defaultViewport: { width: 1366, height: 768 }
})
const page = await browser.newPage()

await page.goto('http://kthornbloom. com/slidetosubmit/')
await page.type('input[name="name"]', ‘Puppeteer Bot')
await page.type('input[name="email"]', 'js@automation.com')

let sliderElement = await page.$('.slide-submit' )
let slider = await sliderElement.boundingBox()

let sliderHandle = await page.$('.slide—submit—thumb' )
let handle = await sliderHandle.boundingBox()

await page.mouse.move(handle.x + handle.width / 2, handle.y + handle.height / 2)
await page.mouse.down()
await page.mouse.move(handle.x + slider.width, handle.y + handle.height / 2, { steps: 10 })
await page.mouse.up()

// success!

await browser.close()
}

run()

完了。こんなに簡単なのです。

Dipbit登録スライダー

Dipbitはデジタル通貨取引サイトです。ログインページと登録ページの両方に「スライドして確認」要素があります。

1_V72pGVtq63udlTwpzYPHuA.png

Dipbitは少し賢くできているので、Puppeteerの実行を隠すコードを追加する必要があります。

const puppeteer = require('puppeteer')

async function run() {
    const browser = await puppeteer. launch({
    headless: false
    defaultViewport: { width: 1366, height: 768 }
})
const page = await browser.newPage()

await page.evaluateOnNewDocument ( () => {
    Object.definaProperty(navigator, 'webdriver', {
         get: () => false
     }}
})

await page.goto('https://www.dipbit.com/auth/login')
await page.type('#email', 'js@automation.com')
await page.type('#password’, 'password123')

let sliderElement = await page.$('.slidetounlock')
let slider = await sliderElement.boundingBox()

let sliderHandle = await page.$('.slidetounlock')
let handle = await sliderHandle. boundingBox()

await page.mouse.move(handle.x + handle.width / 2, handle.y + handle.height / 2)
await page.mouse.down()
await page.mouse.move(handle.x + slider.width, handle.y + handle.height / 2, { steps: 50 })
await page.mouse.up()

// success!

await browser.close()

}

run()

1_JzqzvD-nuy--t7kvfGgu3g.gif

Taobao

タオバオはAlibabaが運営する中国のオンラインショッピングサイトです。Dipbitと同様に登録時にスライダーが表示されます。唯一の違いは登録フォームがiframe内にあることです。しかしPuppeteerにとってそれは問題ではありません。

const puppeteer = require('puppeteer' )

async function run() {
        const browser = await puppeteer. Launch({
        headless: false,
        defaultViewport: { width: 1366, height: 768 }
})
const page = await browser.newPage()

await page.evaluateOnNewDocument(() => {
        Object.defineProperty(navigator, 'webdriver', {
              get: () => false
         })
 ])

await page.goto('https://world.taobao.com/markets/all/sea/register' )

let frame = page.frames()[1]
await frame.waitForSelector('.nc_iconfont.btn_slide')

const sliderElement = await frame.$('.slidetounlock')
const slider = await sliderElement.boundingBox()

const sliderHandle = await frame.$('.nc_iconfont.btn_slide')
const handle = await sliderHandle. boundingBox()
await page.mouse.move(handle.x + handle.width / 2, handle.y + handle.height / 2)
await page.mouse.down()
await page.mouse.move(handle.x + slider.width, handle.y + handle.height / 2, { steps: 50 })
await page.mouse.up()

// success!

await browser.close()

}

run()

1_zQ7Sa5tr295jOxFLWaW1ng.gif

パズル付きスライダーCAPTCHA

「スライドして確認する」Vue コンポーネントに遭遇しました。
人間にとっては簡単で、BOTにとっては難しいはずのものです。

この検証メソッドは画像を取得し、2つのキャンバスと1つのスライダーを作成します。パズルのピースで初期画像をレンダリングします。ユーザーがスライダーを動かすと、パズルのピースが一致します。 2つのピースが一致したらユーザーはスライダーを放す必要があります。これで検証は終了します。

このCAPTCHA はパズルの位置をランダム化してBOTを混乱させます。

1_GMcgXCSRkGW7GORpTh543Q.png

ここではMLやOCRのような派手なことはしたくなかったので、そのスライダーを少しずつ動かして、結果の画像と最初の画像を比較します。

rembrandt.js libraryライブラリを使用して画像を比較します。差が最も小さい画像を見つけたら、スライダーを最適な位置に移動し、マウスを離します。

const puppeteer = require ('puppeteer')
const Rembrandt = ('rembrandt')

async function run () {
         const browser = await puppeteer. Launch({
             headless: false,
             defaultViewport: { width: 1366, height: 768 }
          })
          const page = await browser.newPage()

          let originalImage = ' '

          await page.setRequestInterception(true)
          page.on('request', request => request.continue())
          page.on('response', async response => {
                  if (response. request().resourceType() === 'image')
                       originalImage = await response.buffer().catch(() => {})
          })

          await page.goto('https://monoplasty.github. io/vue—monop lasty-slide-verify/')

          const sliderElement = await page.$('.slide-verify-slider'
          const slider = await sliderElement.boundingBox()

          const sliderHandle = await page.$('.slide-verify-slider—mask-item' )
          const handle = await sliderHandle. boundingBox()

          let currentPosition = @
          let bestSlider = {
               position: 0,
               difference: 100
          }

          await page.mouse.move(handle.x + handle.width / 2, handle.y + handle.height / 2)
          await page.mouse.down()

          while (currentPosition < slider.width - handle.width / 2) {

                 await page.mouse.move(
                           handle.x + currentPosition,
                           handle.y + handle.height / 2 + Math.random() * 10 - 5
                  )

                  let sliderContainer = await page.$('.slide-verify')
                  let sliderImage = await sliderContainer.screenshot()

                  const rembrandt = new Rembrandt ({
                         imageA: originalImage,
                         imageB: sliderImage,
                         thresholdType: Rembrandt. THRESHOLD_PERCENT
                  })

                  let result = await rembrandt.compare()
                  let difference = result.percentageDifference * 100

                  if (difference < bestSlider.difference) {
                      bestSlider.difference = difference
                      bestSlider.position = currentPosition
                  }     

                  currentPosition += 5

          }

          await page.mouse.move(handle.x + bestSlider.position, handle.y + handle.height / 2, { steps: 10 })
          await page.mouse.up()

         // success

         await browser.close()

}

run()

クールな部分を1つ逃した場合。実際のユーザーのマウスの動きをエミュレートするために、Y軸のスライダーの動きをランダム化します😎

await page.mouse.move(
     handle.x + currentPosition,
     handle.y + handle.height / 2 + Math.random() * 10 - 5
)

1_Az5o6MzqAHMzlwTa2_FfnQ.png
1_JymxrPoSiM6uSqegnRbH6Q.png

すべてのコード例はgithub repoにあるので、必要な要素を自由にコピーしてください。

結論

ユーザーエクスペリエンスを向上させるためにCAPTCHAを簡単にバイパスできるようにするか、あるいはBOTから積極的に保護してユーザーエクスペリエンスを低下させるかは、ウェブサイトオーナーがよく悩まされるジレンマです。
ウェブサイトとBOTの間の戦争に終わりはありません。ウェブサイトがどんな検証方法を採用しようとも、誰かがそれを回避する方法を見つけ出すのは時間の問題です。

それにもかかわらず、これは教育目的のためだけのものであり、責任を持ってPuppeteerを使用してください。

同じトピックに関する次のストーリー - JSを使ってGeeTestの「スライダーCAPTCHA」をどのように突破するか

読んでくれてありがとう!記事が気に入っていただけた場合1回、2回、または50回拍手してください。
何か質問がある場合は、下にコメントを残すか、Twitterで話しかけてください。

Discussion

ログインするとコメントできます