TurnstileとCHAPTCHA
今回はTurnstileとCHAPTCHAについて説明しTurnstileをRailsのアプリに導入していきたいと思います。
先日CloudflareからTurnstileという認証テストのβ版が発表され、ユーザーのプライバシーを担保し高速なUXを提供し無料で利用できるということで従来のCAPTCHAの代替となりうるのではないかとされています。TurnstileはCAPTCHAとは違いTurnstileは高性能で不視性を保つ認証テストです。認証はユーザーの目の見えないところで起こり、人間が操作していることを判断の基準にします。
Turnstile は、CAPTCHA の「ユーザーフレンドリーでプライバシーを保護する代替手段」として提示されています。プレス リリースによると、Cloudflare によると、通常、通過に平均 32 秒かかる人物の確認に使用されるインタラクティブなチャレンジが不要になり、プロセス全体が 1 秒に短縮されます
CAPTCHAの背景
CAPTHCAというのはサービスを操作しているのが人間かbotかを判別するための認証方法です。
CAPTCHAを活用すれば、botが行う自動的で行われる大量アクセスを防ぐことができスパムによるサーバーダウンや不正アカウントの作成を防ぐために開発されました。
よくあるのがサービスを使用する際によく歪んでいる文字を入力したり画像の〇〇を選択してくださいのような認証方法だと思います。
CHAPTCHAの問題点~認証~
以下のような認証方法だと一見よくわからない視覚的なパズルや歪んでいる文字を与えられた時に、一分以上もの時間が取られてしまうことがありませんか。
私時々CAPTHCA認証に失敗してしまい、AWSのログイン時に人間であるにも関わらず一定期間ログインできなくなった経験があります。
CHAPTCHAの問題点~プライバシー~
またCAPTCHAはユーザーデータを取り込みプライバシーを犠牲にするという悪評も散見されます。
一般的に使用されているGoogleのreCAPTCHAはGoogleアカウントとの連携を求められユーザー情報をGoogle側に提供してしまうことにつながります。(Googleは広告会社なので)
Turnstileの導入
サイト登録
まず初めにサイトを登録していきます。
今回はQiitaに投稿するということなのでlocalhost
でCloudflareのTurnstileに登録をしました。
※本番環境のサイトキーでlocalhostを登録するのは推奨されていません
次にTurnstileのWidgetの種類を選択しSite KeyとSecret Keyを取得します。
フロントエンド
取得したサイトキーを以下のコードの引数に入れます。
.box-login
.actions
= f.submit "アカウント登録", class: "btn-text"
.cf-turnstile.mb-2{"data-callback" => "javascriptCallback", "data-controller" => "cloudflare-turnstile", "data-sitekey" => "サイトキー"}
.box-newusr
= link_to "ログイン画面へ", user_session_path
バックエンド
バックエンドではprepend_before_actionを使用してユーザー登録前にTurnstileで認証を行わせます。
Turnstileはbefore_actionの前に行うアクションを行う必要があるためprepend_before_actionを使用します。
prepend_before_action :validate_cloudflare_turnstile, only: [:create]
private
def validate_cloudflare_turnstile
validation = CloudflareTurnstile.validate(params[:"cf-turnstile-response"], request.remote_ip)
return if validation
self.resource = resource_class.new sign_up_params
resource.validate
set_minimum_password_length
respond_with_navigational(resource) { render :new }
end
Turnstileをdeviceで使用するためにはroutingを編集してあげる必要があります。
# 変更前
devise_for :users
# 変更後
devise_for :users, controllers: {
registrations: 'users/registrations'
}
validateの論理を作っていきます。
require "net/http"
class CloudflareTurnstile
SECRET_KEY = "シークレットキー".freeze
ALL_HTTP_ERRORS = [Errno::ETIMEDOUT]
def self.validate(payload, remote_ip)
new(payload, remote_ip).validate
end
def initialize(payload, remote_ip)
@payload = payload
@remote_ip = remote_ip
end
def validate
return false unless @payload
response = validate_from_cloudflare
response["success"]
end
private
def validate_from_cloudflare
retries ||= 0
url = URI("https://challenges.cloudflare.com/turnstile/v0/siteverify")
response = Net::HTTP.post_form(url, secret: SECRET_KEY, response: @payload, remoteip: @remote_ip)
JSON.parse(response.body)
rescue *ALL_HTTP_ERRORS
retry if (retries += 1) < 3
end
end
参考サイト