0
0

More than 1 year has passed since last update.

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

Last updated at Posted at 2022-10-11

次のトピックは「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.$('.slidesubmitthumb' )
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で話しかけてください。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0