はじめに
WordPressのプラグインには、管理画面へのログインや、パスワードリセット、記事のコメント投稿など、複数のフォームデータ送信シーンに対してGoogle reCAPTCHAを追加できるものがあります。
しかし、パスワードで保護された記事のログインフォームに対応しているプラグインは、有償のオプション提供のもの以外、私は見つけられませんでした。
このため、WordPressのパスワード保護記事のログイン認証に、reCAPTCHA v3を追加した時の手順をメモします。
前提
<システム環境>
- PHP 7.2
- WordPressバージョン 5.5.1
- WordPressテーマ Twenty Seventeen 2.4 (子テーマを作成してカスタマイズ)
- WordPressプラグイン SiteGuard 1.5.2
(reCAPTCHAの動作に必要ではないのですが、インストールされている環境では考慮点があるため、記載しています)
<その他>
- Google reCAPTCHAキー取得済み。
- 機能は本来プラグイン化する方針だと思うのですが、今回はプラグイン化せず実装しています。
追加手順
Googleの公式マニュアル中、「最も簡単な方法」で追加します。
チャレンジをボタンに自動的にバインド
子テーマのfunctions.phpに下記コードを追加します。
/* GoogleRecaptcha V3 */
function recaptcha_v3() {
wp_enqueue_script( 'recaptcha_v3', 'https://www.google.com/recaptcha/api.js' );
wp_add_inline_script( 'recaptcha_v3', "function onSubmit(token) {
document.getElementById('post-password-form').submit();
}", 'after' );
}
add_action( 'wp_enqueue_scripts', 'recaptcha_v3' );
追加により、reCAPTCHAの動作に必要なJavaScriptがブラウザに読み込まれます。
※パスワード保護ページ以外でも読み込まれます。
getElementByIdの引数('post-password-form')は、パスワード保護ページのログインフォームのIDを指定しています。
ログインフォームのIDは、WordPress標準では明示されていません。このため、後述のコードもfunctions.phpへ追加します。
コード中のget_the_password_form_recaptcha関数は、WordPress標準のwp-includes/post-template.phpのget_the_password_form関数をコピーして変更を加えたもので、ログインフォームの生成を処理しています。
主な変更点は、下記の通りです。
- ログインフォームのIDを宣言する。
- ログインフォームのアクションの送信先をwp-login.phpからwp-loginpp.phpへ変更する。
- ログインフォームのログインボタンにreCAPTCHAの属性(サイトキー含む)を追加する。
- ログインフォームに隠し要素(input hidden)を追加し、g-recaptcha-actionという名前を付与し、アクション名を保持させる。
この変更後の関数をadd_filterの('the_password_form',...)に指定する事で、標準のログインフォームを上書きしています。
/**
* Retrieve protected post password form content.
*
* @since 1.0.0
*
* @param int|WP_Post $post Optional. Post ID or WP_Post object. Default is global $post.
* @return string HTML content for password form for password protected post.
*/
function get_the_password_form_recaptcha( $post = 0 ) {
$post = get_post( $post );
$label = 'pwbox-' . ( empty( $post->ID ) ? rand() : $post->ID );
$output = '<form id="post-password-form" action="' . esc_url( site_url( 'wp-loginpp.php?action=postpass', 'login_post' ) ) . '" class="post-password-form" method="post">
<p>' . __( 'This content is password protected. To view it please enter your password below:' ) . '</p>
<p><label for="' . $label . '">' . __( 'Password:' ) .
' <input name="post_password" id="' . $label . '" type="password" size="20" /></label> <button class="g-recaptcha" data-sitekey="XXX" data-callback="onSubmit" data-action="POST_PASSWORD_FORM">' .
esc_attr_x( 'Enter', 'post password form' ) .
'</button></p><p><input type="hidden" name="g-recaptcha-action" value="POST_PASSWORD_FORM"></p></form>';
/**
* Filters the HTML output for the protected post password form.
*
* If modifying the password field, please note that the core database schema
* limits the password field to 20 characters regardless of the value of the
* size attribute in the form input.
*
* @since 2.7.0
*
* @param string $output The password form HTML output.
*/
return $output;
}
add_filter( 'the_password_form', 'get_the_password_form_recaptcha' );
【要編集】data-sitekey="XXX"のXXXは、事前に取得したreCAPTCHAサイトキーを指定して下さい。外部に公開されるため、間違ってもシークレットキーを指定しないようにご注意下さい。
【要注意】ログインフォームのアクション送信先のURLに
wp-login.phpを指定し、SiteGuardプラグインのログインページ変更機能でログインページURLを変更している場合、変更後のURLが自動的に代入されます。このため、外部から変更後のログインページURLが参照できる状態になるため、本記事ではwp-loginpp.phpと言うファイルを指定しています。ログインページ変更機能を有効にしていない場合も、サーバサイドでreCAPTCHAのスコア解釈を行うため、wp-loginpp.phpファイルの指定が必要です。
この時点で、ログインフォームを表示すると画面の右下に、reCAPTCHAの保護中アイコンが表示されます。
![]() |
---|
※画像は一部マスク処理しています。 |
また、reCAPTCHA管理コンソールを表示すると、サイトからのトラフィックが集計されていることが確認できます。(反映までタイムラグがあります)
![]() |
---|
しかし、サーバ側でスコアの判定処理を記述していないため、このままではチャレンジ結果に応じた処理が行えません。
スコアの解釈
本記事では、スコアの解釈をwp-loginpp.phpファイルを作成して、処理しています。
wp-login.phpの処理にフィルターフックを設けて処理する方法も考えましたが、「図解『wp-login.php』! WordPressのユーザ認証を極める!」1の参考記事のフローを見る限り、パスワードで保護された投稿の認証フローへ適切に挟み込めるフックが見当たりませんでした。
また、WordPress 4.7から追加されたpost_password_requiredフィルタを使い、スコア解釈の判定処理を行う方法も検討しましたが、フック関数のためか、ブラウザからのPOSTリクエストを受け取れず、reCAPTCHAのスコア検証が行えませんでした。余談となりますが、post_password_requiredフィルタはとても便利で、参考記事2で説明されている通り、パスワード保護ページに複数のパスワードを設定する事などが可能です。
wp-loginpp.php
作成したwp-loginpp.phpファイルは下記の通りです。
WordPressインストールディレクトリ(wp-login.phpと同じディレクトリ)に配置します。
// WordPress API
require_once ( dirname(__FILE__) . '/wp-load.php' );
// SiteGuardプラグインのフィルタ無効化
global $siteguard_rename_login;
remove_filter( 'login_init', array( $siteguard_rename_login, 'handler_login_init' ), 10 );
//
// reCAPTCHA判定処理
// 出典:https://www.webdesignleaves.com/pr/plugins/google_recaptcha.php
//
// シークレットキー
$secretKey = 'YYY';
// 認証アクション
$wp_action = isset( $_GET[ 'action' ] ) ? esc_html($_GET[ 'action' ]) : NULL;
// reCAPTCHA トークン
$token = isset( $_POST[ 'g-recaptcha-response' ] ) ? esc_html($_POST[ 'g-recaptcha-response' ]) : NULL;
// reCAPTCHA アクション名
$action = isset( $_POST[ 'g-recaptcha-action' ] ) ? esc_html($_POST[ 'g-recaptcha-action' ]) : NULL;
if ( $token && $action && $wp_action === 'postpass' ) {
// cURL セッションを初期化
$ch = curl_init();
// curl_setopt() により転送時のオプションを設定
// API URL の指定
curl_setopt( $ch, CURLOPT_URL, "https://www.google.com/recaptcha/api/siteverify" );
// POST メソッドを使う
curl_setopt( $ch, CURLOPT_POST, true );
// API パラメータの指定
curl_setopt( $ch, CURLOPT_POSTFIELDS, http_build_query( array(
'secret' => $secretKey, //シークレットキー
'response' => $token //トークン
) ) );
// curl_execの返り値を文字列にする
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
// 転送を実行してレスポンスを $api_response に格納
$api_response = curl_exec( $ch );
// セッションを終了
curl_close( $ch );
// レスポンスの $json(JSON形式)をデコード
$result = json_decode( $api_response );
// 結果の判定処理
if( isset($result->success) && $result->success &&
isset($result->action) && $result->action === $action &&
$result->score >= 0.5
) {
// success が true でアクション名が一致し、スコアが 0.5 以上の場合は合格
// wp_login.phpをロード
require_once ( dirname(__FILE__) . '/wp-login.php' );
} else {
// ログインエラーページへリダイレクト
wp_safe_redirect( esc_url( site_url( 'login-error' ) ) );
}
} else {
// ログインエラーページへリダイレクト
wp_safe_redirect( esc_url( site_url( 'login-error' ) ) );
}
【要編集】$secretKey = 'YYY';のYYYは、事前に取得したreCAPTCHAシークレットキーを指定して下さい。
プログラム中のコメント(出典:)にもある通り、reCAPTCHA判定処理は「Google reCAPTCHA の使い方(v2/v3)」3の参考記事より、ほぼコピーして作成しております。参考記事では、シークレットキーは外部ファイルに格納して、requireする実装になっています。
このプログラムは、大まかに下記の処理を行っています。
- require_onceでWordPress APIを利用可能にします。
- SiteGuardプラグインのログインページ変更機能で使用されるフィルタ 'login_init' を無効化します。有効化されている場合、後述のwp-login.phpをロードした後、URLが無効化されてしまい、404エラーとなります。リダイレクトではなく、サーバーサイドによるrequire_onceによるwp-login.phpの読み込みでもフィルタの対象になります。なお、無効化したフィルタは、次回URL読み込み時には自動で有効化されていたため、本プログラムでは有効化の処理は記述していません。
- URLのGETのactionがpostpass(パスワード保護ページの認証)かを判定します。この判定がないと、不正なURLを指定された場合にログイン画面が表示される恐れがあります。
- reCAPTCHAのスコア判定処理を実行します。ここでは「success が true でアクション名(ログインフォームの隠し要素の値)が一致し、スコアが 0.5 以上の場合は合格」としています。アクション名の比較はPOST値とAPI戻り値を比較しているだけのため、POST値を詐称されれば意味がないかも知れません。気休め程度に割り切っています。
- 合格ではない場合は全て、固定ページのsite_url/login-errorへリダイレクトしています。
エラーページ
WordPressの固定ページで、エラーページを作成します。
URLはsite_url(サイトのベースURL)/login-errorですが、前述のwp-loginpp.phpのリダイレクト先を変更すれば、どのようなURLでも対応可能です。
今回の記事では、reCAPTCHAエラー時の詳細なメッセージを出力する処理は実装せず、単純にreCAPTCHAエラーとだけ表示しています。
![]() |
---|
なお、本エラー画面は、ログインフォームを経由せずに直接site_url/wp-loginpp.phpへアクセスした場合にも表示されます。これにより、SiteGuardプラグインでログインページURLを隠しながらも、パスワード保護記事のログイン認証を実行させる事が可能です。
その他の考慮点
- ブラウザのJavaScriptが無効化されている場合、無条件でreCAPTCHAエラーとなります。JavaScriptが無効化されている場合は、有効化するようにアナウンスを設けるのがフレンドリーかも知れません。
- reCAPTCHA v3による判定に合格しなかった場合は、v2のチャレンジを表示する仕組みを考えても良いかも知れません。
終わりに
今回はWordPressのパスワード保護記事のログイン認証にreCAPTCHA v3を追加する方法を記載しましたが、これがベストプラクティスであるとは残念ながら思っていません。よりより方法がありましたら、ご指摘いただければ幸いです。