コンタクトフォームにreCAPTCHA v3を導入した。

コンタクトフォームからのスパムメールが2日に10通くらいのペースで来ていて、困るほどの数でもないけどどうにかしたいレベルにはなってきたので、reCAPTCHA v3を導入してみました。

「私はロボットではありません」選ぶ必要なし 新「reCAPTCHA」Googleが公開、ユーザーは何もしなくてOK – ITmedia NEWS
reCAPTCHA v3 | reCAPTCHA | Google Developers

Akismetでもよかったのですが、今までコメントフォームに使ってみて誤検知が結構多い印象だったのと、公開されたばかりなので試してみたいこともあり、こっちにしてみました。
判定の流れとしては以下のとおりです。

  1. クライアントサイドでtokenを取得
  2. 取得したtokenを元にサーバサイドでverify用のAPIを呼び出す
  3. APIから返ってきた結果をもとに、ページにアクセスしてきたのが人間かロボットかを判定する

クライアントサイドの導入方法

  1. 管理ページでAPI実行用のキーを発行する。
  2. 管理ページにjsロード用のscriptタグのスニペットが表示されるので、reCAPTCHAしたいページのhead内に埋め込む。
  3. 管理ページにreCAPTCHAのexecute用のscriptタグのスニペットが表示されるので、reCAPTCHAしたいページのどこかしらに埋め込む
<script>
  grecaptcha.ready(function() {
    grecaptcha.execute('Site key', {action: 'action_name'}) // action_nameはサーバサイドの処理で使用する
      .then(function(token) {
        // Verify the token on the server.
    });
  });
</script>

サーバサイドの導入方法

  1. クライアントからtokenを受け取る。
  2. Secret keyとtokenを使用してスコアの問い合わせを行う。APIからのレスポンスの形式はドキュメントを参照
curl https://www.google.com/recaptcha/api/siteverify  -X POST -d "secret=secret&amp;response=token"
{
    "success": true,
    "score": 0.9,
    "action": "contact_form", // クライアントサイドで決めたaction_name
    ...
}

しきい値を決めて、それを下回るならエラーを返す等の処理をサーバサイドに書きます。

コンタクトフォームはContact Form 7を使用しているのですが、そのうちプラグイン側がv3に対応しそうなのと、自分で改造するのが面倒なので、reCAPTCHA用のエンドポイントを設置して、「エンドポイントからのレスポンスが200以外だったら、送信ボタンのDOMをremoveした上でsubmitイベントを無効化する」とかいう意味があるんだかないんだかわからない処理を入れておきました。
DOMの処理終わる前に送信されたら意味ないし、そもそもスパムの送信元がJS無効化してたら詰むんだけどまぁものは試しなので…。
reCAPTCHA用のjsも本当は全ページに読み込ませないといけないのですが、スパムの送信元はどうせコンタクトフォームに直接アクセスしてきてるので、コンタクトフォームのページにだけ読み込ませています。

設置してから4日くらい経ちますが、スパムが3通くらいしか届いていないのである程度効果はあるのかなと思っています。

追記 2018/12/15

Contact Form 7がv3に対応したので用済みになりました。