Google reCAPTCHA v3をRailsアプリに導入する

Google reCAPTCHA v3

www.google.com

導入の流れ

  1. サイトを登録してサイトキーとシークレットキーを発行する
  2. フロントエンドでsubmit時にトークンを発行する
  3. サーバサイドでスコアを評価する

フロントエンドでsubmit時にトークンを発行する

ログインフォームにreCAPTCHAのトークン用のフィールドを追加し、ログイン時にreCAPTCHAのトークンを発行して、ログインパラメータと合わせてサーバ側に送信するようにします。
(以下のサンプルはslimで書かれています)

= form_with url: signup_path, local: true, html: { name: "signupForm" } do |f|
  = f.email_field :email, required: true
  = f.password_field :password, required: true
  = hidden_field_tag :g_recaptcha
  a#signupSubmit ログイン

script src="https://www.google.com/recaptcha/api.js?render=reCAPTCHA_site_key"

javascript:
  grecaptcha.ready(() => {
    let form = document.querySelector("[name=signupForm]");
    if (!form) { return; }

    form.querySelector("#signupSubmit").addEventListener("click", () => {
      grecaptcha.execute("reCAPTCHA_site_key", { action: "login" }).then((token) => {
        const recaptchaField = document.querySelector('#g_recaptcha');
        recaptchaField.value = token;
        form.submit();
      });
    });
  });

developers.google.com

サーバサイドでスコアを評価する

以下のmoduleをapp/controllers/concern等に配置し、controllerでincludeして利用します。

module GoogleRecaptcha
  VERIFY_ENDPOINT = "https://www.google.com/recaptcha/api/siteverify"

  def recaptcha_valid_token?(token, min_score)
    result = recaptcha_verify(token)
    Rails.logger.info result
    return false if !result["success"]

    result.fetch("score", 0.0) >= min_score
  end

  def recaptcha_verify(token)
    req = new_verify_request(token)
    http = Net::HTTP.new(verify_url.host, verify_url.port)
    http.use_ssl = verify_url.is_a?(URI::HTTPS)
    res = http.request(req)
    return { "success" => false } if res.code != "200"
    JSON.parse(res.body)
  end

  def verify_url
    @verify_url ||= URI.parse(VERIFY_ENDPOINT)
  end

  def new_verify_request(token)
    req = Net::HTTP::Post.new(verify_url)
    req.set_form_data(verify_parameters(token))
    req
  end

  def verify_parameters(token)
    {
      secret: "reCAPTCHA_secret_key",
      response: token,
    }
  end
end

controller側のサンプルコードはこちらです。

class SessionsController < ApplicationController
  include GoogleRecaptcha

  # GET /signin
  def new; end

  # POST /sessions
  def create
    if recaptcha_valid_token?(params[:g_recaptcha], 0.5)
      # ログイン処理
    else
      # reCAPTCHAのスコアがしきい値より低かった場合の処理
    end
  end
end

developers.google.com