概要
みなさんこんにちはcandleです。公に開かれたフォームはスパムや総当り攻撃を受けやすいです。
そこで、画像認証を導入して、悪意のあるフォームを拒否してみましょう。
simple-captchaは本当にシンプルで、表示される画像は大文字のアルファベットのみです。これは画像認証が読みづらくて、ユーザーが離れる事を避ける事ができる一方で、セキュリティ的には多少低いでしょう。下の図をみて、もっと複雑な画像認証がしたい場合は他の方法を試してください。
前提
railsの環境が整っている
twitterbootstrap gem を使用した環境で説明して行きます。twitterbootstrap gem事態はデザインのライブラリみたいなものなので、必ずしも入れる必要はありません。
simple-captcha gemをインストールする
Gemfileに下の記述を書きましょう。
gem 'simple_captcha', :git => 'git://github.com/galetahub/simple-captcha.git'
保存したら、gemをインストールします。
bundle install
gemインストール完了です。
simple-captchaのセットアップ
simple-captchaはフォームで画像認証された値をデータベースにあるデータと参照して認証をしています。そのため、simple-captcha用のテーブルを用意します。
下のコマンドでsimple-captchaを作成します。
rails generate simple_captcha
マイグレーションファイルをデータベースに反映させます。
rake db:migrate
次に、app/controllers/application.rb のApplicationControllerクラスの中に下のソースを貼り付けます。
include SimpleCaptcha::ControllerHelpers
これでセットアップ完了です。
サンプルのscaffoldを作成する
下のコマンドを実行してください。
rails g scaffold Memo title:string description:text rake db:migrate
Memo scaffoldが完成しました。
twitterbootstrap gemを反映させる
もしも、twitterbootstrap gemが入っているなら、下のコマンドでスタイルを反映させます。
rails generate bootstrap:install static rails g bootstrap:layout application rails g bootstrap:themed Memos
テーマが反映されました。
ビューを変更する
フォームに関するビューを変更します。今回の例で言えば、memosの_form.html.erbを変更します。app/views/memos/_form.html.erbを開いて、フォームタグの中に下の記述を書きます。
<div class="form-group"> <%= f.label "Simple-Captcha",:class => 'control-label' %> <div class="controls"> <%= f.simple_captcha :label => "上の文字を記入してください",:placeholder => "ここに入力" %> </div> </div>
下のように表示されたでしょうか?
ビューの変更は以上です。
モデルを編集する
form_forを使用してフォームを作った場合、データベースのカラムと紐付いていない値をフォームから投げる場合は擬似的な値を受け取ることを宣言しなくてはいけません。
simple-captchaから送られてくるcaptcha_key,とcaptchaを受けれる様に、モデルに必要事項を記述します。
今回の例で説明しますと、app/models/memo.rbを開いて、下の記述を
class Memo < ActiveRecord::Base end
↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
class Memo < ActiveRecord::Base attr_accessor :captcha_key, :captcha end
にして保存しましょう。
コントローラーを変更する
ストロングパラメーターで値を許可する
シンプルキャプチャのフォームから送られてきた値を受けるために、
ストロングパラメーターで許可する必要があります。下の2つの値を許可しましょう。
:captcha_key, :captcha
例えば、memosコントローラーmemo_params関数の場合は下のようになります。
def memo_params params.require(:memo).permit(:title, :description,:captcha_key, :captcha) end
フォームの値を受けるアクションを編集
フォームから送られてきた値を処理してデータベースに収めるアクションの中に、下の文章を書いて、画像認証の判定をします。
if simple_captcha_valid? #データベースに値を収める処理 end
もしも、このif文が正しく通れば、画像認証ができているということです。
例えば、scaffoldの場合、createdアクションにこのif文を適応させたければ、下の様にします。
def create @memo = Memo.new(memo_params) if simple_captcha_valid? respond_to do |format| if @memo.save format.html { redirect_to @memo, notice: 'Memo was successfully created.' } format.json { render action: 'show', status: :created, location: @memo } else format.html { render action: 'new' } format.json { render json: @memo.errors, status: :unprocessable_entity } end end else render action: 'new' end end
simple_captcha_valid?をオーバライドする
ここからが本番です。実はこのsimple_capcha_valid?関数はsimple-captcha gemに書かれているのですが、この関数がフォームから送られてくる値の参照し方が下のようになっています。
params[:captcha] params[:captcha_key]
ところが、form_forから送られて来る場合は下のような参照の仕方をしないといけません。
:memoの記述は各自のコントローラーの名前になります。
params[:memo][:captcha] params[:memo][:captcha_key]
つまり、編集しないと正しい値をsimple-captcha gemが取得できないのです。
gemを直接書き換えるのはよくないといわれています。
なので、このsimple_captcha_valid?関数をオーバライドして使えるようにしましょう。
config/environment.rbを開きます。ファイルの一番下に下の記述をコピペします。
何度も言いますが、今回のサンプルを使っていない場合は[:memo]のところは各々のコントローラー名にしてください。
#simplecaptchaのオーバーライド module SimpleCaptcha module ControllerHelpers def simple_captcha_valid? return true if Rails.env.test? if params[:memo][:captcha] data = SimpleCaptcha::Utils::simple_captcha_value(params[:memo][:captcha_key] || session[:captcha]) result = data == params[:memo][:captcha].delete(" ").upcase SimpleCaptcha::Utils::simple_captcha_passed!(session[:captcha]) if result return result else return false end end end end
保存したら、サーバを再起動しましょう。
これで、simple-captchaが使えます。
実際に使ってみる
新しいmemoを作成する画面に行き、正しい値を入力します。
「Create Memo」を押すと、入力した値が正しければ、データベースに新しいレコードが保存されます。
認証できました。
まとめ
form_forを使うと少々面倒な事になりますが、これでなんとか画像認証ができます。
もしも、これよりも良い方法がある場合はコメント欄などによろしくお願いします。