WordPressのGoogle reCAPTCHA for MW WP FormをCloudflare Turnstileに変更、迷惑メール対策の方法とOrbStackのDocker利用について
WordPressやスクラッチのフォームでの迷惑メール対策として、Google reCAPTCHAをボット検証で利用するにはいくつかの問題点がありました。Google reCAPTCHA for MW WP Formでの検証も、問い合わせフォームの入力に時間がかかるとタイムアウトエラーとなりますので独自のカスタマイズが必要でした。
今年からGoogle reCAPTCHAの無料枠が大幅に引き下げられました。
WordPressのMW WP Formで問い合わせフォームを運用中の全体のアクセス数がとても多いサイトで、Google reCAPTCHAの無料枠の制限数を超えたため、スパム送信されるサイトがありました。突破されると巡回されてしまうので、あらためて、Cloudflare Turnstileを導入しました。
Cloudflare Turnstileを導入して迷惑メール対策、
MW WP Formのバリデーションルールをカスタム追加
Clourdflare Turnstileをフォームに設置して、バリデーション時にトークンを検証する手順をご紹介します。Google reCAPTCHAやTurnstileを設置するだけで検証していないフォームを、時折ですが見かけることがあります。
どちらのサービスも検証をしなければ、フォームの脆弱性が問題となります。
Turnstile利用時は、siteverify APIによるトークンのサーバーサイド検証を必ず実行するようにしてください。
ここで紹介するのは下記の手順です。
WordPressのプラグイン MW WP Formで利用する方法ですが、PHPやJavascriptなどで利用するのはとても簡単ですので、公式ドキュメントを参考に是非ご利用ください。
Cloudflare Turnstileのアカウント作成
Clourdflareのチュートリアルにそってアカウント作成、ウィジェットの作成を行なって、サイトキーとシークレットキーを作成しておきます。
Cloudflare Docs: Get started with Turnstile
https://developers.cloudflare.com/turnstile/get-started/
Cloudflare Docs: Client-side rendering
https://developers.cloudflare.com/turnstile/get-started/client-side-rendering/
headにCloudflare TurnstileのCSS, Javascriptを追加
こちらもチュートリアルに沿ってCSSとJavascriptをリンクしてください。
header.phpでturnstileのコードを、問い合わせフォームのページを判定して読み込みます。
<head>
<meta charset="<?php bloginfo( 'charset' ); ?>">
<!-- Cloudflare Turnstile -->
<?php if ( is_page( '<問い合わせフォームのスラッグ>' ) ) : ?>
ここにTurnstileのCSSとJavascriptを記述する
.
.
.
<?php endif; ?>
<?php wp_head(); ?>
</head>
CSSとJavascriptはクライアント側レンダリングの「Turnstileウィジェットを暗黙的にレンダリングする」デモページから最新のURLをコピーペーストしてください。
cloudflare/turnstile-demo-workers
https://github.com/cloudflare/turnstile-demo-workers/blob/main/src/implicit.html
※ bootstrap.min.css を読み込むとスタイルが変わってしまうなど不具合があるようでしたら、記述不要です。基本は、api.jsだけ読み込めば表示されます。
Turnstileのウィジェットをフォームに設置
MW WP Formのフォームに下記のコードを記述すると、Cloudflare Turnstileのウィジェットが表示されます。
<div class="cf-turnstile" data-sitekey="<your site key>"></div>
検証メッセージの多言語対応
多言語の場合は言語指定の属性を指定すると、検証メッセージ「成功しました!」が翻訳されます。
<div class="cf-turnstile" data-sitekey="<your site key>" data-language="<language code (4 letters) >"></div>
Cloudflare Docs: Supported languages
https://developers.cloudflare.com/turnstile/reference/supported-languages
MW WP Formのカスタムバリデーションを作成
公式チュートリアルの「サーバーサイド検証」に沿って、バリデーションクラスを作成します。
Cloudflare Docs: Server-side validation
https://developers.cloudflare.com/turnstile/get-started/server-side-validation/
下記はコード例です。
使い方を読んでいただき、適宜テストしながら書き換えてご利用いただけます。
class.turnstile.phpとして、wp-content/plugins/mw-wp-form/classes/validation-rulesに設置するとMW WP Formのバリデーションとして組み込まれます。
使い方にも記述していますが、シークレットキーとsiteverify API URLを記述しておきます。
※MW WP Formを確認画面 + 完了画面の3画面で利用する想定でテストしています。
MW WP Formのバリデーションで、Cloudflare Turnstileの検証が失敗する場合への対応
確認画面に遷移するバリデーションが実行される時、rule() プロセスが何度か呼ばれることがあります。
Cloudflare Turnstileは短期間に何度も検証すると「timeout or duplicate」のエラーとなります。これを回避するために、1度の遷移でプロセスが複数発生していても、バリデーション処理を1度だけ通るようにPOSTデータにキーを設定してフラグ処理しています。
この処理以外はチュートリアルの通りで、とても簡単な検証プロセスです。
<?php
/**
* Cloudflare Turnstileのバリデーション
*
* バリデーション名: turnstile
* ※$nameとファイル名class.turnstile.phpは全部小文字で「turnstile」
*
* 使い方
* Cloudflare Turnstileのアカウントでウィジェットを作成し、サイトキーを取得してください。
*
* 1. フォームに下記を追加
* [mwform_hidden name="cont-turnstile" value="1"]
* [mwform_error keys="cont-turnstile"]
* <div class="cf-turnstile" data-sitekey="<your site key>"></div>
*
* 2. mwform_validation_mw-wp-form-<form id>に「turnstile」を追加
* 例)$validation->set_rule( 'cont-turnstile', 'turnstile', array( 'message' => '迷惑ボットとしてブロックされました' ) );
*
* 3. プロパティ$urlにCloudflare TurnstileのURL、$secretにシークレットキーを記入
*
*
* 備考
* MW WP Formのリダイレクトで複数回実行されるため、POSTデータに $process_key の値があればバリデーションしない
* ※複数回Turnstileの検証を実行すると「timeout or duplicate」エラーが発生する
*
* テスト
* ※デバッグモードで検証レスポンスを出力します。
* - 他画面からの遷移の場合はバリデーションしない
* - 確認画面への遷移の場合はバリデーションを一回だけ実行
* - 送信完了画面への遷移の場合はバリデーションしない
* - 問い合わせ画面で「お問い合わせ」へのリンクをクリックして遷移した場合はバリデーションしない
* - 確認画面で「お問い合わせ」へのリンクをクリックして遷移した場合はバリデーションを実行しない
*
* @package mw-wp-form
* @author pane brut
* @license GPL-2.0+
*
*/
/**
* MW_WP_Form_Validation_Rule_Turnstile
* MW WP Formのカスタムバリデーションクラス Cloudflare Turnstile 検証ルール
*/
class MW_WP_Form_Validation_Rule_Turnstile extends MW_WP_Form_Abstract_Validation_Rule {
/**
* Validation rule name.
*
* バリデーションルールとしてユニークな名前を設定
* WARNING: ファイル名は$nameと一致していないとエラー無しで実行できない
*
* @var string
*/
protected $name = 'turnstile';
/**
* Cloudflare Turnstile siteverify URL.
*
* 公式サイトから最新のURLを取得
*
* @var string
*/
protected $url = '<siteverify URL>';
/**
* Cloudflare Turnstile secret key.
*
* @var string
*/
protected $secret = '<your secret key>';
/**
* Process key.
*
* 複数回処理されないようPOSTデータに設定するプロセス開始フラグのキー
*
* @var string
*/
protected $process_key = 'MWWP-pb-turnstile-check';
/**
* Validation process.
*
* @param string $name Validation name.
* @param array $options Validation options.
* @return string
*/
public function rule( $name, array $options = array() ) {
// POSTデータ無しはバリデーション不要
if ( ! $this->Data->gets() ) {
return;
}
// 入力画面での修正/送信完了画面への遷移はバリデーション不要
if ( 'back' === $this->Data->get( 'submitBack' ) || in_array( 'send', $this->Data->gets() ) ) {
return;
}
// 複数回実行されるのを回避:既にこのプロセスが処理されていればバリデーションしない
$process_check = $this->Data->get( $this->process_key );
if ( ! empty( $process_check ) ) {
return;
}
// バリデーションプロセス開始をPOSTデータに設定
$this->Data->set( $this->process_key, true );
// POSTデータにTurnstileのトークンが設定されていれば検証を実行
$turnstile_token = $this->Data->get( 'cf-turnstile-response' );
if ( $turnstile_token ) {
// リクエスト body を設定
$post_fields = array(
'secret' => $this->secret,
'response' => $turnstile_token,
'remoteip' => $_SERVER['REMOTE_ADDR'],
);
// 検証リクエストを実行
$response = wp_remote_post( $this->url, array( 'body' => $post_fields ) );
$response_data = json_decode( wp_remote_retrieve_body( $response ), true );
// Cloudflare Turnstile 検証成功
error_log( get_class( $this ) );
error_log( print_r( $response_data, true ) ); // debug: 検証レスポンスを出力
if ( $response_data['success'] ) {
return;
}
}
// バリデーションエラー
$defaults = array(
'message' => '迷惑ボットとしてブロックされました',
);
$options = array_merge( $defaults, $options );
return $options['message'];
}
/**
* Add setting field to validation rule setting panel.
*
* @param numeric $key ID of validation rule.
* @param array $value Content of validation rule.
* @return void
*/
public function admin( $key, $value ) {
return false;
}
}
MW WP Formのバリデーションを設定
フォームには下記のhidden input を追加してください。このフィールドをバリデーションに使います。エラー表示する場合は、mwform_errorも追加してスタイルを調整してください。
※nameはお好みで書き換えてご利用ください。
[mwform_hidden name="cont-turnstile" value="1"]
[mwform_error keys="cont-turnstile"]
フィルターフックのmwform_validation_mw-wp-form-<form id>で、上記のhidden input 要素のnameキーにバリデーションルールをセットします。
$validation->set_rule( 'cont-turnstile', 'turnstile', array( 'message' =>'迷惑ボットとしてブロックされました' ) );
バリデーション実行時、ボットとしてTurnstile検証エラーになるか?
ブラウザでエラー表示を確認しましょう
簡単なテストを実施するために、ブラウザの検証ツールで、テスト用のボット風デバイスを作成しておきます。この例では「ユーザーエージェント文字列」に存在しない適当な名前を設定しました。
Chromeのデバイス設定例
デバイスの検証で、問い合わせフォームにアクセスすると、ボットと疑われてチャレンジが表示されることを確認できます。
チャレンジに表示されたチェックボックスをチェックせず、「入力内容を確認する」ボタンをクリックすると、バリデーションが実行されます。
ボットとしてバリデーションエラーが表示されました!
デバイスをiPhoneなどに戻して、検証ツールを閉じます。
今度は人間としてフォームにアクセスして、無事にバリデーションがエラーなく通ることを確認します。
WordPressのフォーム MW WP Formでも導入
迷惑メール対策に、Cloudflare Turnstileはすばらしい!
フォームのボット検証として自然な感じで、設置も簡単です。WordPressのフォーム類での迷惑メール対策として、Google reCAPTCHAの代替えにご利用ください。
CloudflareはCDNサービスとしてもおなじみ。WordPress向けのサービスも充実していますのでご存じの方も多いと思います。安定したボット検証が無料で提供されていることに感謝します。
ログインフォーム、コメントフォーム用などコア機能用にはサードパーティ製のプラグインがあります。
https://wordpress.org/plugins/simple-cloudflare-turnstile/
自社Webサイトにスパムが来てお困りの方は、放置しておくとサーバー運用に影響が大きいため早急に対策を実施することをおすすめします。
詳しい対応方法などは当方まで気軽にご相談ください。
MW WP Formは開発終了なので他のフォームプラグインに置き換えています
MW WP Formの開発はセキュリティー対応以外は停止されたとのことですので、順次、フォームを置き換える改修が必要です。
MW WP Formはカスタマイズの柔軟性があり、数々の高機能なフォームを設定してきました。
今まで、本当にありがとうございました。
Macのローカル開発環境 DockerはOrbStackが速い!
Turnstileとは関係ないのですが、今日のひとこと。
本日、Macの開発環境をDocker DesktopからOrbStackに乗り換えました。マイグレーションツールがあるので、Docker環境の移行作業はとても簡単です。
※クローンされるので作業ストレージの空き容量には要注意です。
今までの苦労がなんだったのか!? というぐらい速くなりました。
最近はサイトによってインフラ環境がマチマチになりがちなので、Dockerでサーバー環境をわけていましたが、内蔵の空き容量が少ないせいか・・・ボリュームマウントしているとDocker desktopの動作が異常に遅くて使いものにならず。
XAMPPと使い分ける日々、XAMPPからは外付けディスク上のファイルにアクセスする方法がなく、環境の切り替えもかなり面倒でしたが・・・今日からはDockerもノーストレス。
下記の公式ドキュメントのとおり”Switching from Docker Desktop is 100% seamless”、おすすめです!
OrbStack Docs: Install OrbStack
https://docs.orbstack.dev/install