はじめに
今回はBot等に自動認証を許さないために用いられる「CAPTCHA」をRailsアプリケーションに実装してみます。
ImageMagickあたりでつまずいたので、備忘録感覚にメモ。
環境確認とアプリの作成
まずは動作環境の確認とサンプルサプリケーションの作成を行っていきます。
現在普及している Rails 4系 を使っていきます。
$ cat /etc/redhat-release CentOS release 6.8 (Final) $ rails _4.2.0_ new app
(失敗)『simple-captcha』のセットアップ
本記事でメインとなるキャプチャのライブラリですが、上位の人気を誇っている「simple-captcha」を入れてみました。
$ vim Gemfile (追加) gem 'simple_captcha', :git => 'git://github.com/galetahub/simple-captcha.git'
そしてインストールを実行します。
# bundle install
ここで問題が発生しました。
$ rails generate simple_captcha Running via Spring preloader in process 11946 identical app/views/simple_captcha/_simple_captcha.erb /opt/rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/bundler/gems/simple-captcha-2602bf19a63d/lib/generators/simple_captcha_generator.rb:18:in `create_migration': wrong number of arguments (given 3, expected 0) (ArgumentError) from /opt/rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/railties-4.2.0/lib/rails/generators/migration.rb:63:in `migration_template' from /opt/rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/bundler/gems/simple-captcha-2602bf19a63d/lib/generators/simple_captcha_generator.rb:19:in `create_migration'
https://github.com/pludoni/simple-captcha/issues/18
上記URL先を見てみると、新バージョンである『SimpleCaptcha2』を使えば解決できそうな予感。
『SimpleCaptcha2』のセットアップ
$ vim Gemfile (追加) gem 'simple_captcha2', git: 'https://github.com/pludoni/simple-captcha.git', require: true
そして再度インストール
$ bundle install
改めて「rails generate simple_captcha」コマンド
$ rails generate simple_captcha Running via Spring preloader in process 1067 Expected string default value for '--helper'; got true (boolean) Expected string default value for '--jbuilder'; got true (boolean) create app/views/simple_captcha/_simple_captcha.erb create db/migrate/20170112000239_create_simple_captcha_data.rb
様々なファイルが作成されたということで今回は成功したらしい。
db/migrate/
に下記のようなマイグレーションファイルが作成されたことが確認できます。
$ cat db/migrate/20170112000239_create_simple_captcha_data.rb class CreateSimpleCaptchaData < ActiveRecord::Migration def self.up create_table :simple_captcha_data do |t| t.string :key, :limit => 40 t.string :value, :limit => 6 t.timestamps end add_index :simple_captcha_data, :key, :name => "idx_key" end def self.down drop_table :simple_captcha_data end end
下記コマンドでテーブルを作成します。
$ rake db:migrate == 20170112000239 CreateSimpleCaptchaData: migrating ========================== -- create_table(:simple_captcha_data) -> 0.0026s -- add_index(:simple_captcha_data, :key, {:name=>"idx_key"}) -> 0.0012s == 20170112000239 CreateSimpleCaptchaData: migrated (0.0040s) =================テーブルが正常に作成されたことを確認して、下記のように `application.rb` コントローラに「include SimpleCaptcha::ControllerHelpers」の一行を追記します。
$ vim app/controllers/application.rb class ApplicationController < ActionController::Base # Prevent CSRF attacks by raising an exception. # For APIs, you may want to use :null_session instead. protect_from_forgery with: :exception include SimpleCaptcha::ControllerHelpers end
テストアプリを作成
ここでは簡単にRESTアプリを作成します。
# rails g scaffold Memo title:string description:text # rake db:migrate
ビューを変更
下記のようにキャプチャ入力項目を追記します。
$ vim app/views/memos/_form.html.erb (省略) <div class="field"> <%= f.label :description %><br> <%= f.text_area :description %> </div> <div class="field"> <%= f.label "Simple-Captcha" %><br> <%= f.simple_captcha :label => "上の文字を記入してくだい", :placeholder => "ここに入力" %> </div> <div class="actions"> <%= f.submit %> </div> (省略)
ブラウザでキャプチャの確認
ここまで進めますと「http://localhost:3000/memos/new
」にアクセスするとキャプチャ入力項目が表示されるはずですので、確認してみます。
キャプチャ画像が表示されていません・・・。
サーバ側のログを確認すると以下のようなエラーが表示されています。
StandardError (Error while running convert: convert: not authorized `ACTWH' @ error/constitute.c/ReadImage/453. convert: missing an image filename `jpeg:-' @ error/convert.c/ConvertImageCommand/3015. ): /opt/rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/bundler/gems/simple-captcha-b145495ab9e5/lib/simple_captcha/utils.rb:17:in `run' /opt/rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/bundler/gems/simple-captcha-b145495ab9e5/lib/simple_captcha/image.rb:83:in `generate_simple_captcha_image' /opt/rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/bundler/gems/simple-captcha-b145495ab9e5/lib/simple_captcha/middleware.rb:42:in `make_image' /opt/rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/bundler/gems/simple-captcha-b145495ab9e5/lib/simple_captcha/middleware.rb:21:in `call' (以下省略)
キャプチャ画像を表示
画像の加工処理がうまくいっていないようですので、その処理に必要なパッケーシをインストールします。
# yum -y install ghostscript # yum -y install ImageMagick-devel
ここでブラウザで確認してみても画像が表示されていません。 http://www.srcw.net/wiki/index.php?SimpleCaptcha
調べてみるとImageMagickに脆弱性があり、設定ポリシーが厳しくなりデフォルト設定だと表示されていないと推測。
その脆弱性に関する情報はこちらを参照
ImageMagickの脆弱性(CVE-2016-3714他)についてまとめてみた - piyolog
その設定ファイルである「ポリシーファイル」を以下のように編集してみます。
$ vim /etc/ImageMagick/policy.xml (省略) <policymap> <!-- <policy domain="system" name="precision" value="6"/> --> <!-- <policy domain="resource" name="temporary-path" value="/tmp"/> --> <!-- <policy domain="resource" name="memory" value="2GiB"/> --> <!-- <policy domain="resource" name="map" value="4GiB"/> --> <!-- <policy domain="resource" name="area" value="1gb"/> --> <!-- <policy domain="resource" name="disk" value="16eb"/> --> <!-- <policy domain="resource" name="file" value="768"/> --> <!-- <policy domain="resource" name="thread" value="4"/> --> <!-- <policy domain="resource" name="throttle" value="0"/> --> <!-- <policy domain="resource" name="time" value="3600"/> --> <!-- <policy domain="coder" rights="none" pattern="EPHEMERAL" /> <policy domain="coder" rights="none" pattern="HTTPS" /> <policy domain="coder" rights="none" pattern="HTTP" /> <policy domain="coder" rights="none" pattern="URL" /> <policy domain="coder" rights="none" pattern="FTP" /> <policy domain="coder" rights="none" pattern="MVG" /> <policy domain="coder" rights="none" pattern="MSL" /> <policy domain="coder" rights="none" pattern="TEXT" /> <policy domain="coder" rights="none" pattern="LABEL" /> <policy domain="path" rights="none" pattern="@*" /> --> </policymap>
編集を終えたら再度ブラウザでアクセスし確認します。
今度はうまく表示できているようです。
ですが肝心なキャプチャをデタラメな値を入力しても作成できるようになっています。
正常に動作させるためにモデルとコントローラの編集を行います。
正常に動作させるためモデルとコントローラを編集
モデル
「attr_accessor :captcha_key, :captcha」の一行を追記します。
$ vim app/models/memo.rb class Memo < ActiveRecord::Base attr_accessor :captcha_key, :captcha end
コントローラ
末尾にあるmemo_params関数の中身を以下のように編集します。
「, :captcha_key, :captcha
」を追加
$ vim app/controllers/memos_controller.rb (省略) def memo_params params.require(:memo).permit(:title, :description, :captcha_key, :captcha) end end
同じコントローラファイルに入力されたキャプチャ文字列が正しいかの検証処理をcreateメソッド内に記述します。 以下のように編集します。
$ vim app/controllers/memos_controller.rb # POST /memos # POST /memos.json 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 :show, status: :created, location: @memo } else format.html { render :new } format.json { render json: @memo.errors, status: :unprocessable_entity } end end else redirect_to :action => "new" end end
最後に入力された値の引き渡しがうまくいくように下記のコードを記述します。
$ vim config/environment.rb 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
これで入力された文字列と画像に表示されている文字列の比較が行われ、等しい場合に正常な処理が行われるようになります。