はじめに (対象読者・この記事でわかること)

この記事は、Webフォームの実装や自動化テストに取り組む開発者、特にCloudflare Turnstileを導入しているサイトの開発者を対象としています。Cloudflare Turnstileは、プライバシーを尊重するCAPTCHAソリューションとして人気がありますが、自動化テストや特定のユーザーフローを実装する際には、プログラムからそのボタンを操作する必要が生じることがあります。

本記事を読むことで、JavaScriptを使ってCloudflare Turnstileのボタンをプログラムからクリックする方法を理解し、実際のコードを実装できるようになります。また、自動化テスト環境での活用方法や、実装中に遭遇する一般的な問題とその解決策についても学べます。

前提知識

この記事を読み進める上で、以下の知識があるとスムーズです。

  • HTML/CSSの基本的な知識
  • JavaScriptの基本的な知識とDOM操作の理解
  • Cloudflare Turnstileの基本的な実装方法
  • Promiseやasync/awaitの非同期処理の理解

Cloudflare Turnstileの基本とプログラム操作の必要性

Cloudflare Turnstileは、従来のCAPTCHAに代わるプライバシー尊重型のチャレンジソリューションです。ユーザーは「私はロボットではありません」といった単純なチェックボックスをクリックするだけで認証を完了できます。しかし、自動化テストや特定のユーザーフローを実装する際には、このボタンをプログラムから操作する必要が生じます。

例えば、E2Eテストでフォームの送信を自動化する場合や、特定の条件下でTurnstileの認証をスキップしたい場合などが考えられます。Turnstileのボタンは通常のボタンと異なり、セキュリティ上の理由から直接操作することが難しいため、特別なアプローチが必要です。

JavaScriptでCloudflare Turnstileのボタンをクリックする具体的な方法

ステップ1: Turnstileの基本実装

まずは、Cloudflare Turnstileの基本的な実装方法を確認しましょう。以下はHTMLでの基本的な実装例です。

Html
<!DOCTYPE html> <html> <head> <title>Turnstile Example</title> <script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script> </head> <body> <form id="myForm"> <div class="cf-turnstile" data-sitekey="your_site_key"></div> <button type="submit">Submit</button> </form> </body> </html>

この実装では、cf-turnstileというクラスを持つdiv要素がTurnstileのチャレンジ領域として表示されます。

ステップ2: JavaScriptからTurnstileを操作する方法

Turnstileのボタンをプログラムから操作するには、いくつかの方法があります。ここでは、最も一般的な2つの方法を紹介します。

方法1: TurnstileのAPIを利用する方法

CloudflareはTurnstileの操作を容易にするためのAPIを提供しています。以下に、Turnstileのトークンを取得して送信する例を示します。

Javascript
// Turnstileの初期化 turnstile.render('#turnstile-container', { sitekey: 'your_site_key', callback: function(token) { // トークンが取得できたときの処理 console.log('Token:', token); document.getElementById('token-input').value = token; document.getElementById('myForm').submit(); } }); // プログラムからTurnstileをトリガーする関数 function triggerTurnstile() { // TurnstileのDOM要素を取得 const turnstileContainer = document.querySelector('.cf-turnstile'); // Turnstileのiframeを取得 const turnstileIframe = turnstileContainer.querySelector('iframe'); // iframe内のボタンをクリック if (turnstileIframe) { const iframeDocument = turnstileIframe.contentDocument || turnstileIframe.contentWindow.document; const button = iframeDocument.querySelector('button'); if (button) { button.click(); } } } // ボタンにイベントリスナーを追加 document.getElementById('manual-trigger').addEventListener('click', triggerTurnstile);

方法2: 自動化テストフレームワークを利用する方法

SeleniumやPlaywrightなどの自動化テストフレームワークを使用している場合、これらのツールが提供する機能を利用してTurnstileを操作できます。以下はPlaywrightを使用した例です。

Javascript
const { chromium } = require('playwright'); (async () => { const browser = await chromium.launch(); const page = await browser.newPage(); // Turnstileを含むページにアクセス await page.goto('https://your-website.com'); // Turnstileのiframeに切り替え const frame = await page.frame('turnstile_iframe'); if (frame) { // iframe内のボタンをクリック await frame.click('button'); // トークンが取得されるまで待機 const token = await page.waitForSelector('#token-input', { state: 'attached' }); const tokenValue = await input.inputValue(); console.log('Turnstile token:', tokenValue); // フォームを送信 await page.click('#submit-button'); } await browser.close(); })();

ステップ3: カスタム実装とトークンの取得

Turnstileのボタンをクリックした後、実際の認証処理が行われ、トークンが生成されます。このトークンを取得してサーバーに送信する必要があります。以下は、カスタム実装とトークン取得の例です。

Javascript
// Turnstileの初期化 let turnstileToken = null; turnstile.render('#turnstile-container', { sitekey: 'your_site_key', callback: function(token) { // トークンを取得 turnstileToken = token; console.log('Turnstile token obtained:', token); // トークンをフォームに設定 document.getElementById('token-input').value = token; // 必要に応じてフォームを送信 // document.getElementById('myForm').submit(); }, 'expired-callback': function() { // トークンの有効期限が切れた場合の処理 console.log('Token expired'); turnstileToken = null; } }); // カスタムトリガーボタン document.getElementById('custom-submit').addEventListener('click', function() { if (!turnstileToken) { // トークンが取得されていない場合、Turnstileをトリガー triggerTurnstile(); // トークンが取得されるまで待機(非同期処理) const checkToken = setInterval(() => { if (turnstileToken) { clearInterval(checkToken); document.getElementById('myForm').submit(); } }, 100); } else { // トークンが既に取得されている場合、直接フォームを送信 document.getElementById('myForm').submit(); } });

ステップ4: サーバーサイドでのトークン検証

クライアントサイドで取得したTurnstileトークンは、サーバーサイドで検証する必要があります。以下はNode.jsを使用したサーバーサイドでの検証例です。

Javascript
const express = require('express'); const fetch = require('node-fetch'); const app = express(); app.use(express.urlencoded({ extended: true })); app.use(express.json()); app.post('/verify', async (req, res) => { const { token } = req.body; const secretKey = 'your_turnstile_secret_key'; try { const response = await fetch(`https://challenges.cloudflare.com/turnstile/v0/siteverify`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ secret: secretKey, response: token, }), }); const data = await response.json(); if (data.success) { // トークンが有効な場合の処理 res.status(200).send('Verification successful'); } else { // トークンが無効な場合の処理 res.status(400).send('Verification failed'); } } catch (error) { console.error('Error:', error); res.status(500).send('Server error'); } }); app.listen(3000, () => { console.log('Server running on port 3000'); });

ハマった点やエラー解決

問題1: iframe内の要素にアクセスできない

Turnstileはiframe内で実装されているため、セキュリティ上の制約により直接DOMにアクセスすることができません。これにより、document.querySelectorなどを使っても要素を取得できないことがあります。

解決策

この問題を解決するには、自動化テストフレームワークが提供する機能を利用するか、Turnstileの公式APIを使用します。PlaywrightやSeleniumなどのツールは、iframe内の要素にアクセスするための専用のAPIを提供しています。

Javascript
// Playwrightを使用したiframe内の要素へのアクセス例 const frame = await page.frame('turnstile_iframe'); if (frame) { await frame.click('button'); }

また、Turnstileのrenderメソッドにコールバック関数を渡すことで、トークンが取得されたときに処理を実行できます。

問題2: トークンの取得タイミング

非同期処理のために、トークンが取得される前にフォームが送信されてしまうことがあります。

解決策

トークンが取得されるまで待機する処理を実装します。Promiseやasync/awaitを利用すると、コードがより読みやすくなります。

Javascript
function waitForTurnstileToken() { return new Promise((resolve, reject) => { if (turnstileToken) { resolve(turnstileToken); } else { const interval = setInterval(() => { if (turnstileToken) { clearInterval(interval); resolve(turnstileToken); } }, 100); // タイムアウト処理(例: 30秒) setTimeout(() => { clearInterval(interval); reject(new Error('Turnstile token timeout')); }, 30000); } }); } // 使用例 document.getElementById('custom-submit').addEventListener('click', async function() { try { if (!turnstileToken) { triggerTurnstile(); await waitForTurnstileToken(); } document.getElementById('myForm').submit(); } catch (error) { console.error('Error:', error); alert('認証に失敗しました。再度お試しください。'); } });

問題3: 複数のTurnstileインスタンスの扱い

ページに複数のTurnstileインスタンスがある場合、どのインスタンスを操作するか特定する必要があります。

解決策

各Turnstileインスタンスに固有のIDを設定し、そのIDを基に操作対象を特定します。

Javascript
// 複数のTurnstileインスタンスを初期化 turnstile.render('#turnstile-container-1', { sitekey: 'your_site_key_1', callback: function(token) { console.log('Token from container 1:', token); document.getElementById('token-input-1').value = token; } }); turnstile.render('#turnstile-container-2', { sitekey: 'your_site_key_2', callback: function(token) { console.log('Token from container 2:', token); document.getElementById('token-input-2').value = token; } }); // 特定のインスタンスを操作する関数 function triggerSpecificTurnstile(containerId) { const container = document.querySelector(containerId); const turnstileIframe = container.querySelector('iframe'); if (turnstileIframe) { const iframeDocument = turnstileIframe.contentDocument || turnstileIframe.contentWindow.document; const button = iframeDocument.querySelector('button'); if (button) { button.click(); } } }

まとめ

本記事では、JavaScriptでCloudflare Turnstileのボタンをプログラムからクリックする方法を解説しました。

  • Turnstileの基本実装とAPIの利用方法
  • 自動化テストフレームワークを活用した操作方法
  • トークンの取得とサーバーサイドでの検証
  • 実装中に遭遇する問題とその解決策

この記事を通して、読者はCloudflare Turnstileをプログラムから操作する技術を習得し、自動化テストの効率化や特定のユーザーフローの実装ができるようになったことでしょう。今後は、より複雑なユーザーフローへのTurnstileの統合方法や、大規模なアプリケーションでの最適化手法についても記事にする予定です。

参考資料