ボットからサイトを守るための仕組みを提供してくれる、googleのサービス、reCAPTCHAについて調べたのでメモ。
今回調べたのは、reCAPTCHAのinvisible。1画面で複数のrecaptchaが必要なケースをrailsで実装します。※画面側については、基本的にはjavascriptなので言語関係なく使える内容になっていると思います。
導入手順
公式サイトを参考。キーを発行するだけ。数分で終わります。
「sign up for an API key pair」のリンクからキーを発行
https://developers.google.com/recaptcha/intro
実装方法
3つあります。
https://developers.google.com/recaptcha/docs/invisible
・Automatically bind the challenge to a button
・Programmatically bind the challenge to a button
・Programmatically invoke the challenge.
今回は、「Programmatically invoke the challenge」でやっています。
理由は、1画面で複数のrecaptchaが必要な時でも対応できるようにするため。
詳細は以下をみていただければわかるかもしれません。
recaptchaをverify(検証)するまでの流れ
1. 画面にrecaptchaを埋め込む
2. google側にリクエストを送信。(grecaptcha.execute)
3. 2.のレスポンスを受け取る。レスポンスの中のtokenが、g-recaptcha-response
という画面フィールドにセットされる。
4. g-recaptcha-response
にセットされているtokenを検証してボットかどうか判断。
実装
View
<! --
// 1画面にformAとformBがある場合
// formAとformBにそれぞれあるSubmitボタン(POST)で別々の処理をさせたい
-->
<form id="form-a">
<div class="g-recaptcha" data-callback="formASubmit"></div>
<button type="button" onclick="grecaptcha.execute($('#form-a').find('.g-recaptcha').data('recaptcha-widget-id'));">A_OK</button>
</form>
<form id="form-b">
<div class="g-recaptcha" data-callback="formBSubmit"></div>
<button type="button" onclick="grecaptcha.execute($('#form-b').find('.g-recaptcha').data('recaptcha-widget-id'));">B_OK</button>
</form>
<!-- callback関数を定義 -->
<script type="text/javascript">
// grecaptcha.executeを実行すると対応するcallbackが呼ばれる
// recaptcha callback formA
function formASubmit(token) {
$('#form-a').submit();
}
// recaptcha callback formB
function formBSubmit(token) {
$('#form-b').submit();
}
</script>
<!--
// 以下に記載の箇所は、後に記載するgemを使用する場合、invisible_recaptcha_tagsをオーバーライドして定義しても思います。
// その場合 <%= invisible_recaptcha_tags %> とView側で定義するだけでOKです。
-->
<!-- recaptchaをレンダリング -->
<script src="https://www.google.com/recaptcha/api.js?onload=recaptchaCallback&render=explicit" async defer></script>
<script type="text/javascript">
var captchaWidgetIds = [];
var recaptchaCallback = function() {
// g-recaptchaクラスの箇所にrenderします。
$('.g-recaptcha').each(function(index, el) {
// sitekeyはgoogle側で発行したものを指定
// badgeはrecaptchaのロゴを表示する位置
// callbackはinvisible recaptchaでは必須。 $(el)としているのは、1画面で複数recaptchaがあり、別名のcallbackを呼べるようにするため
var widgetId = grecaptcha.render(el, {'sitekey' :'xxxxxxxx','size' : 'invisible','badge' : 'inline', 'callback' : $(el).data('callback')});
$(el).data('recaptcha-widget-id', widgetId);
captchaWidgetIds.push(widgetId);
});
};
</script>
画面からGETする時の場合
// recaptcha callback
function onSubmit(token) {
window.location.href = "http://test" + "?g-recaptcha-response=" + token;
}
Controller
gemを使う
https://github.com/ambethia/recaptcha
def verifyRecaptchaToken
@model = Model.new
# gemが提供しているメソッド。
# tokenを検証し問題があれば@modelにセットします
verify_recaptcha(model: @model)
# @modelをみてverify結果を確認
if @model.errors.empty?
return true
else
return false
end
end
def verifyRecaptchaToken
token = params[:"g-recaptcha-response"]
# googleに問い合わせた結果のtokenを検証する
# @return [Boolean] verifyが成功すればtrue、そうで無いならfalse
url = "https://www.google.com/recaptcha/api/siteverify"
response =Net::HTTP.post_form URI(url),
{"secret":"xxxxxx", "response":token}
if response.code == '200'
result = JSON.parse(response.body)['success']
else
result = false
end
return result
end
以上です。
シンプルな作りであれば実装方法で挙げた3つの方法のうち、他の2つの方法が簡単です。
ただし1画面で複数recaptchaを使用したい場合は、今回記載した方法にしておくと良いと思います。