<Hazm Blog />

Rails7にRecapchaV3を導入した

Cover Image for Rails7にRecapchaV3を導入した
Hazm
Hazm

導入の経緯

Googleカレンダー集計サービスの StackerPro で、会員登録フォームを設置しています。
Eメールアドレスとパスワードを設定するタイプかつ、Railsフレームワークでよく使われるDevise Gemを使用したのごく一般的な構成の機能です。
そのせいもあり、Botからのスパム登録が数十件という単位で発生していました。

システム自体にバグが存在しないと仮定すればそれがシステム全体に悪影響を及ぼすことはありませんが、
なんとも気持ちが悪いのでBotからの登録を排除したいと考えました。

対処方法について検討する

まず、どのような対策が取れるかを検討しました。
パッと思いつく限りで以下の方法が考えられます。

  1. 人間にしかわからない回答フォームを用意する(1+1=)等
  2. WAFの導入
  3. recapcha認証の導入

1. 人間にしかわからない回答フォームを用意する

今回のような、全サイトに手当たり次第に攻撃しようとするBotは全てのサイトに適した入力内容で登録しようとはしてきません。
なので、たとえば「1+1の答えを入力してください」等の回答フォームを用意し、サーバー側で回答が合っているかをチェックすればほとんどのBotを弾くことができます。

ですが、何の学習にもならないのでこの方法はやめました。

2. WAFの導入

WAFを導入すれば、Botの判定をWAFがやってくれます。
アプリケーションの前段でBotを排除できますし、システムを健全に保ってくれますが、問題は料金面です。
現時点で自分しか使わないし課金プランも用意していないため、運営を続けることを視野に入れた時、あまり大きな費用をかけることは避けたいと考えます。

良い案ではありますが、別の手段を検討します。

3. recapcha認証の導入

recapchaとは、フォームにBot判定をするJSを埋め込み、判定結果を一緒にPostすることでBot判定をしてくれるものです。
幸いなことに、Googleのrecapchaは無料で利用できますし、多くのサイトで採用実績がありそうです。
また、個人的にrecapchaの導入をしたことがなかったので学習にもなるなと思いました。

今回はrecapchaの導入によってBotの登録を排除したいと思います。

Image

Railsにおけるrecapchaの導入

ruby gem として recapcha gem が提供されています。
ambethia/recaptcha

gemの役割としては、埋め込むjsのビューヘルパーの提供と、コントローラ側で結果を判定するメソッドの提供です。
つまり、ビュー側にタグを設置し、コントローラ側に判定処理を追加してあげればOK!というイメージですね。  

ポイントとしては、Rails7ではTurbo、Rails6以前ではTurbolinksを使用している場合、ビューヘルパーのturboオプションを利用する必要があります。 そうしないと、うまく認証結果を得られず、フォームで判定結果を送信できませんので注意が必要です。

<%= recaptcha_v3(action: 'signup', turbo: true) %>

recapchaV2とrecapchaV3

Googleの提供するrecapchaには、V2とV3があり、V2はよくあるチェックボックスをユーザーがクリックするか、類似する画像をタイルの中から選ぶようなものです。対してV3は、そのようなユーザの操作が不要になったバージョンになります。

V3の場合、ユーザはrecapcha認証を行うために何も意識することがないため、フォームの見た目も変更せずに判定を行えるので、今回もV3を利用したいと思いました。 しかし、V3でもユーザーを誤ってBotとして判断してしまう可能性があるため、GemのREADMEにはV2へのフォールバックを実装するように案内されています。

しかしながら、現在V2とV3の共存ができないようです。色々と試したのですが、どうもinvalid siteとなってしまいます。 なので今回はV3一本で、Bot判定された場合には連絡先へのコンタクトを案内する形にしました。

Image1

実装してみる

それでは実際に実装してみます。

gem "recaptcha", "~> 5.16"

ビューにrecapchaタグを挿入しました。

  <%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
    ...
    <%= recaptcha_v3(action: 'signup', turbo: true) %>
    ...
  <% end %>

Device Gemを使用しているので、コントローラを生成してコントローラクラスをオーバライドしました。

class Users::RegistrationsController < Devise::RegistrationsController
  prepend_before_action :check_captcha, only: [:create]

  ...略...

  private

  def check_captcha
    success = verify_recaptcha(action: 'signup', minimum_score: 0.5)
    Rails.logger.info "success=#{success}"
    if success
      # success
    else
      redirect_to root_path, notice: "Botと判定されました。もし人なら、ごめんなさい。再度試してください。それでも上手くいかないなら、画面下部の連絡先にご連絡ください。"
    end
  end

  ...略...

end

導入結果

Webブラウザからの登録はリクエストが通りますが、Botからの登録を完全に排除できています。

recapchaの導入にあたっては、V2とV3の共存ができず苦戦したのと、turboオプションを知らなかったが故にハマったところがありましたが、 結果的には数行の追加でrecapchaを導入できました。