CSRF対策を行うprotect_from_forgeryメソッド

こんにちは!kossyです!




さて、今回はprotect_from_forgeryメソッドが何をしているのか気になったので、
ブログに残してみようかと思います。



環境
Ruby 2.5.1
Rails 5.1.6
MacOS Mojave



CSRFってなんぞや

CSRF(クロスサイトリクエストフォージェリ)とは、Webサイトの脆弱性をつくようなサイバー攻撃の一種で、
サイトに攻撃用のコード(主にjavascript)を埋め込むことで、
アクセスしてきたユーザに対して意図しない操作を行わせる攻撃のことを言います。

www.itmedia.co.jp
mixi、懐かしいですね。。。


そんなことはさておき、
CSRF攻撃を受けることで、SNS等で意図しない書き込みが行われたり、
ECサイトで勝手に商品購入をさせられたりといった事象が発生します。



Railsで行われているCSRF対策

Railsでは、

app/controllers/application_controller.rb

class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception
end
app/views/layouts/application.html.haml(今回はhamlで記述しています)

!!!
%html
  %head
    %meta{:content => "text/html; charset=UTF-8", "http-equiv" => "Content-Type"}/
    %title アプリ名
    = csrf_meta_tags

省略

という記述がデフォルトでされており、
特に意識することなくCSRF対策を行うことができます。

ただ、htmlタグだけでフォームやリンクを生成すると、
対策の恩恵を受けることができません。


フォーム生成の際はビューヘルパーを使え

フォームやリンクを生成するときは、
form_forやlink_to等のビューヘルパーを利用するようにしましょう。
これによって、アプリ側でトークンと呼ばれる証明書のようなランダムな文字列が生成され、
フォームに埋め込まれるようにできます。

以下はform_forを用いたフォーム画面の例と、
ページのソースを表示したものを一部抜粋して記載しています。

_form.html.haml

  = form_for [@review] do |f|
    .form-group
      = f.label 'タイトル'
      = f.text_field :bookname, class: 'form-control', placeholder: 'Type a bookname'
    .form-group
      = f.label '言語'
      = f.select :language, [["",""], ["HTML&CSS","HTML&CSS"], ["Ruby","Ruby"]], {}
    .form-group
      = f.label 'レベル'
      = f.select :level, [["",""], ["初級","初級"], ["中級","中級"], ["上級","上級"]], {}
    .form-group
      = f.label '画像'
      = f.file_field :image
    .form-group
      = f.label '本文'
      = f.text_area :content, class: "form-control form-textarea"
    = f.submit '投稿', class: "btn btn-primary"
chromeの「ページのソースを表示」機能で展開されたソースコード

<!DOCTYPE html>
<html>
<head>
<meta content='text/html; charset=UTF-8' http-equiv='Content-Type'>
<title>FriendlyReview</title>
<b><meta name="csrf-param" content="authenticity_token" />
<meta name="csrf-token" content="D0b+EkIWQ2kuSoVqp2mzED5lsJP/hzsr8yugtFGaQ+XBC..." /></b>

省略

<form class="new_review" id="new_review" enctype="multipart/form-data" action="/reviews" accept-charset="UTF-8" method="post">
<input name="utf8" type="hidden" value="&#x2713;" />
<input type="hidden" name="authenticity_token" value="kHbl258N5/V5eChnjoZ8P1QhionN978xO..." />

authenticity_tokenの後に続くvalueトークンになっています。
Railsでは、リクエスト処理時にアプリ側で保持しているトークンと、
リクエスト情報として送信されるトークンと比較し、
一致していればそれ以降の処理を行います。
もし一致していない、またはトークンそのものが存在しないと、
ActionController::InvalidAuthenticity Token という例外を発生します。







まとめ

・HTMLタグだけでフォームを作るな、ビューヘルパーを使え
・レイアウトファイルにcsrf_meta_tagsの記載を忘れるな
・HTTP GET によるリンクでデータの操作をしない

CSRF対策における注意事項は上記3点です。





参考にさせていただいた記事

Rails セキュリティガイド - Railsガイド

carrierwave + rmagickで画像アップロード機能の実装

こんにちは!kossyです!





本日の気になったニュースはこちら
https://headlines.yahoo.co.jp/hl?a=20181012-00050002-yom-bus_allheadlines.yahoo.co.jp

資本主義である以上寡占化するのは当たり前な気もします。

どんどん世の中を便利にしてくれているIT企業の活動を国が邪魔するのは
どうなの?と思ってしまいます。

年明けにも、とのことなので、続報に期待です。










さて、今回はcarrierwave + rmagickで画像のアップロード機能の実装手順を
ブログに残してみたいと思います。



環境
Rails 5.1.6
Ruby2.5.1
carrierwave (1.2.3)
rmagick (2.16.0)
MacOS
Homebrew(1.7.2)




※画像を持たせたいモデルに既にカラムを持たせてあることを前提に話を進めます。
(micropostモデルにimageカラム等)


imagemagickの導入
画像のリサイズに必要なので導入します。

$ brew install imagemagick


gemfile編集
画像アップロード機能を実現してくれるcarrierwaveと、
imagemagickと共に画像のリサイズに必要なrmagickを導入します。

gemfile

gem 'carrierwave'
gem 'rmagick'

$ bundle install


image_uploader.rbの作成

ファイルのアップローダーを追加するために、
ターミナルで下記コマンドを実行します。

$ rails g uploader image

作成されたapp/uploaders/image_uploader.rbに
以下のコードを記述をします。

app/uploaders/image_uploader.rb

include CarrierWave::Rmagick

image_uploader.rb内の任意の場所に以下を記述
process resize_to_fit: [800, 800]

省略


モデルファイルでマウント

その後、画像データを持たせるモデルファイルに
以下の記述を追加します。

mount_uploader :image, ImageUploader

これでモデル側の準備は完了です。

ビューには、
新規投稿作成画面に、
f.file_field等のフォームヘルパーを用いて、ファイル投稿のフィールドを作成します。

コントローラでは、ストロングパラメーター内でファイルパラメータを許可するよう実装します。



これで、簡易的な画像の投稿機能は実装できます。

マイグレーション実行時に用いるupとdownメソッド

こんにちは!kossyです!




気になったニュースはこちら
headlines.yahoo.co.jp

運営チームのガイドライン違反かどうかの確認がとても大変な気もしますが、
どのようにこの仕組みを回すのか気になります。

pythonと画像認識のライブラリを使って実装するのでしょうか。
画像認識や機械学習は手をつけていない範囲なので、
基礎教養として体系的な知識は身につけておきたいところです。












さて、今回はup,downメソッドの役割についてブログに残してみたいと思います。

環境
Rails5.1.6
Ruby2.5.1
MacOS





Railsマイグレーションを実行する際に登場するメソッドになります。
Railsにはrails db:rollbackというありがたいコマンドがあり、
これを実行するとマイグレーションをひとつ前の状態に戻してくれます。

ここでupとdownが登場するのですが、
up,downメソッドとchangeメソッドで挙動が異なります。

up,downの場合
upとdownメソッドは、rails db:migrateの実行時と、rails db:rollbackの実行時の処理を定義します。

class ChangeColumnToReview < ActiveRecord::Migration [5.0]
  def up
    add_column :review, :content, :string
  end

  def down
    remove_column :review, :content
  end
end

upメソッドで変更したものは、downメソッドで元に戻るようにしておかないと、正しくrails db:rollbackできません。







changeの場合

changeメソッドの場合は、rails db:migrate実行時の処理を記述します。
up、downメソッドの挙動と異なる点は、rails db:rollbackの処理を記述しなくても、
反転した処理を自動で行ってくれる点にあります。

class AddColumnToReview < ActiveRecord::Migration [5.0]
  def change
    add_column :review, :content, :string
  end
end

rollback時の処理も行ってくれるchangeメソッドですが、
全てのケースにおいて使えるわけではありません。






up.downメソッドのユースケース
例えば、以下のマイグレーションファイルをrails db:rollbackしようとすると、失敗します。

class RemoveColumnToReview < ActiveRecord::Migration [5.0]
  def change
    remove_column :review, :content
  end
end

contentカラムの情報がどこにも書いていないからです。
カラムの型情報も存在しないため、カラムの作成ができません。

こういった場合に、up,downメソッドを使用します。

class ChangeColumnToReview < ActiveRecord::Migration [5.0]
  def up
    remove_column :review, :content
  end

  def down
    add_column :review, :content, :string
  end
end

Named Scopeの使い方

こんにちは!kossyです!




就活ルールについての続報が出てました。
https://headlines.yahoo.co.jp/hl?a=20181009-00000081-asahi-bus_allheadlines.yahoo.co.jp

既にルールそのものが形骸化しているのに、
国が躍起になってルールを守ろうとするのはどうしてなんでしょうかね。
気になります。














さて、今回はRailsでDB検索を行う際に
よく利用する条件句を予め準備する、
名前付きスコープ(Named Scope)について、
ブログに残したいと思います。


環境
Rails5.1.6
Ruby2.5.1
MacOS


名前付きスコープとは

データベース検索のコードを書いていると、同じ条件のコードを何度か書くことがあります。
そのような場合に、名前付きスコープを用いると便利です。
名前付きスコープは、特定の条件式やソート式などを予めモデル側で名前付けすることで、
利用時に名前ですぐに呼び出せるようにしておく仕組みです。

任意のモデルファイル

scope :名前,  -> { 条件句 }

以下は例です

review.rb
省略
scope :revieworder, -> { order(id: 'DESC') }


reviews_controller.rb
省略

def index
  @review = Review.all.revieworder
end

モデルファイルで名前付きスコープを定義することで、
そのままメソッドチェーンとして連鎖させることができます。

上記の例は簡単なものでしたが、
もっと複雑な検索コードになりそうな時に使うとよさそうです。



参考にさせていただいた記事
qiita.com

簡単! Railsで過去の日付に対するバリデーションを設定してみた

こんにちは!kossyです!




ペット市場が盛り上がっているようです。
https://headlines.yahoo.co.jp/hl?a=20181007-00010001-kantele-bus_allheadlines.yahoo.co.jp

1兆5000億という数字自体はめちゃくちゃ市場規模が大きいわけではないですが、
順調に成長している市場は魅力的ですね。

私もペットを飼っていた時がありますが、
ペットに対しては財布の紐も緩みがちだったので、
お金をかけてしまう人たちの気持ちはよくわかります。

まだまだ伸びるんじゃないでしょうか。











さて、今回は入力された日付が過去の日付だった場合に弾くような
バリデーションの実装方法をブログに残したいと思います。



環境
Rails5.1.6
Ruby2.5.1
MacOS




実装方法

addメソッドを利用して、特定の属性に関する任意のエラーメッセージを付与しています。
if文は後置ifの形を取って、カラムの値がnilまたはDateクラスのtodayメソッドを使って取得した本日の日付と、
date型のカラム名に渡ってきた値を比較して、
本日の日付よりも前の値だった場合に弾くように実装しています。

app/models/任意のモデルファイル.rb

validate :pretend_ago

def pretend_ago(メソッド名は任意)
    errors.add(:date型のカラム名, '表示させたいエラーメッセージ') if date型のカラム名.nil? || date型のカラム名 < Date.today
end

なぜかはわかりませんが、

validates :pretend_ago

とするとエラーになりました。

エラーメッセージの日本語化は
kossy-web-engineer.hatenablog.com
こちらのエントリが参考になるかと思います。



参考にさせていただいた記事
Active Record バリデーション | Rails ガイド
日付の前後を比較するバリデーションを書く - ブログ

長いテキストを省略できるRailsのビューヘルパー'truncate'

こんにちは!kossyです!






やり込んだゲームボーイソフトを思い出しました。
news.yahoo.co.jp

まだ年齢が一桁だった頃ですが、母親がゲーム好きだった影響で、
ポケットモンスターシリーズ
スーパーマリオランド2 6つの金貨
茶々丸冒険記3 アビスの塔
ロックマンワールド5
なんかをやり込んだ記憶があります。
昔のゲームは難易度が高くて面白かったですね。

スマホでも高難度の横スクロールアクションゲームが楽しめるのでしょうか。













さて、今回は長いテキストを省略できるビューのヘルパーである、
'truncate'の使い方について、ブログに残してみたいと思います。



環境
Rails5.1.6
Ruby2.5.1
MacOS
Haml





ビューヘルパーってなに?

Railsにおいてビューヘルパーとは、
日付の表示の仕方の変換や、HTMLのフォームを生成するなどの役割があります。
ビューヘルパーを用いることで、可読性の向上が期待できます。


truncateの使い方

truncateは、長いテキストを省略して表示することができるビューヘルパーです。

任意のビューファイル
= truncate("lorem ipsum dolor sit amet", length: 15)
=> lorem ipsum ...

デフォルトの設定は省略記号が...で30文字に省略されますが、
それぞれomissionとlengthオプションで任意の設定にできます。

また、ブロックを用いることで、省略後の処理を記述できます。

= truncate(review.content, length:15) do
  = link_to "続きを読む", review_path(review)

上記コードは、ビューヘルパーであるlink_toを用いて、
コンテンツ部分を省略しつつ、レビュー詳細画面へのリンクを設定しています。





参考にさせていただいたサイト・記事
truncate - リファレンス - - Railsドキュメント
Rails - 長い文字列を省略して表示する

セキュリティ対策ができているかわかるgem'brakeman'の使い方

こんにちは!kossyです!



気になったニュースはこちら
headlines.yahoo.co.jp

自動運転にはやはりC++が使われるのでしょうか。
どのようにデータのやりとりを行うかが気になります。

また、新会社の持ち株比率がソフトバンクの方が上回っている点に驚きました。
どのように交渉が行われたのでしょうか。
続報に期待です。














さて、今回は、セキュリティ対策が適切に行われているかわかる
Railsのgem'brakeman'の使い方について、ブログに残してみたいと思います。

環境
Rails5.1.6
Ruby2.5.1
MacOS



brakemanの導入

導入も実行もめちゃくちゃ簡単です。

gemfile

group develoment do
  省略
  gem 'brakeman', require: false
end

ターミナル

$ bundle install

$ bundle exec brakeman

Loading scanner...
と表示された後に、結果が表示されます。

== Brakeman Report ==

Application Path: /Users/user1/project/applicatin
Rails Version: 5.1.6
Brakeman Version: 4.3.1
Scan Date: 2018-10-05 05:14:19 +0900
Duration: 1.250086 seconds
Checks Run: BasicAuth, BasicAuthTimingAttack, ~~~以下省略

== Overview ==

Controllers: 4
Models: 4
Templates: 33
Errors: 0
Security Warnings: 0

== Warning Types ==


No warnings found

どうやらセキュリティ上の問題はないらしいです。



詳しいオプションはGithubのドキュメントを和訳している優しい方がいらっしゃいました。
qiita.com



参考にさせていただいた記事
Brakeman を使って Rails プロジェクトのセキュリティインシデントを防ぎましょう - Sider Blog
Brakeman Railsのセキュリティチェックを行うためのGem | 酒と涙とRubyとRailsと
BrakemanでRailsセキュリティチェックを行う - さかなソフトブログ

flash[:notice]とflash.nowの違い

こんにちは!kossyです!



さて、今回はRailsでフラッシュメッセージを表示したい時に用いる、
flash[:notice]とflash.nowの違いについてブログに残してみたいと思います。

環境
Rails5.1.6
Ruby2.5.1
MacOS
haml



flashとは
例えば、ユーザーがアプリケーションに初めてログインした場合や、メッセージの投稿に成功した場合等に、
1回限りのインスタントメッセージを表示する仕組みをflashと呼びます。
基本的なflashの使い方は、コントローラでflashメッセージをkeyとvalue形式で設定し、ビューからflashをハッシュのように呼び出します。
また、flashは基本的にリダイレクトと同時に使うものなので、redirect_toメソッドを一緒に使うことが多いです。





flash[:notice]の使い方

まずは、viewにフラッシュメッセージを表示するように記述します。

application.html.haml

省略

%body
  - flash.each do |key, value|
    = content_tag :div, value, class: key
  = yield

flashメッセージの部分を部分テンプレート化するのもいいでしょう。

_flash.html.haml

.notification
  - flash.each do |key, value|
    = content_tag :div, value, class: key
application.html.haml
%body
  = render 'layouts/header'
  = render 'layouts/flash'
    = yield

次に、controller側で処理を記述します。
今回は、Reviewモデルのインスタンスを削除するdestroyメソッドが呼ばれた時を想定しています。

reviews_controller.rb

  def destroy
    @review = Review.find(params[:id])
    @review.destroy!
      redirect_to root_path, notice: '投稿の削除に成功しました!'
  end

削除に成功した場合、ルートパスにリダイレクトし、リダイレクト先でnoticeのメッセージ部分を表示するように記述しています。
redirect_toメソッドの場合、flash[:notice]とすることなく、引数にnoticeというキーを渡すだけで、
フラッシュメッセージを表示することができます。
参考URL: flashを使って簡易メッセージを表示する – Ruby on Rails 始めました






flash.nowの使い方
flash.nowを使うシチュエーションは、リダイレクトをしない場合です。
通常flashはリダイレクトを伴って利用しますが、リダイレクトを利用しなくても利用可能です。
その場合、flash[:notice]の代わりに、flash.nowを使います。
例として、レビューの投稿に失敗した時のエラーメッセージをflash.nowで表示してみます。

reviews_controller.rb

def create
  @review = Review.new(review_params)
    if @review.save!
      redirect_to root_path
    else
      flash.now[:alert] = '投稿に失敗しました'
      render :new
    end
end



違いはなんなのか
なぜリダイレクトをしない場合に異なる記述をするかというと、
flashメッセージの表示期間に関わっています。flashを使った場合のメッセージの表示期間は次のリクエストまでです。
現在のリクエストとリダイレクトのリクエスト分がメッセージの表示期間と合致するため、リダイレクト先でflashメッセージが表示されます。

対して、flash.nowの表示期間は現在のリクエストまでです。
リダイレクトを伴わない場合(上記のコードで言うと、render :newを使っているところ)、
flash[:notice]としてしまうと、メッセージは表示されるのですが、メッセージの表示期間は次のリクエストまでなので、
必要以上にメッセージが表示されてしまいます。





まとめ

redirecto_toを使う場合

redirect_to パス名, notice: "エラーメッセージ"

renderと共に使う場合

flash.now[:alert] = "エラーメッセージ"
render :アクション名

と覚えておけばいいでしょう。

複数形がわかるpluralize

こんにちは!kossyです!


スルガ銀行に関して続報がありました。
headlines.yahoo.co.jp

スルガ銀行は他の地銀を上回る高収益体質で、
前の金融長官から「地銀の成功モデル」と褒められていましたが、
不正あっての高収益だったのかと思うとなんとも言えない気持ちになります。


どんな不正が発生していたのか、
現場の実情などはこちらの動画で詳しく紹介されています。
www.youtube.com













さて、今回は英単語を複数形に変換できるメソッド、pluralizeについてブログに残してみたいと思います。



環境
Rails5.1.6.
Ruby2.5.1
MacOS







設定よりも規約が優先される
Railsにはいくつか基本理念がありますが、
重要なもののひとつに「設定よりも規約が優先される」という理念があります。


Railsでは、Webアプリケーションで行われるさまざまなことを実現するための最善の方法を明確に思い描いており、
Webアプリケーションの各種設定についても従来の経験や慣習を元に、それらのデフォルト値を定めています。
このようにある種独断でデフォルト値が決まっているおかげで、開発者の意見をすべて取り入れようとした
自由過ぎるWebアプリケーションのように、開発者が延々と設定ファイルを設定して回らずに済みます。
出典: Rails をはじめよう | Rails ガイド
例えば、コントローラやビュー、モデルの命名規則などがこれに当たります。



pluralizeの役割
コントローラは複数形で表現されます。
なので、英単語の複数形で記述する必要があるのですが、
英単語の複数形は英語ネイティブでない人にはわからない時があります。
そんな時に扱うのがpluralizeメソッドです。

[1] pry(main)> "person".pluralize
=> "people"

まぁその都度ググればいいだけの話なのですが、こういうやり方もあるということで、頭の片隅に入れておいても
いいかもしれません。
ちなみに、'singularize'メソッドを使うと、英単語を単数形に変換してくれます。

[2] pry(main)> "people".pluralize
=> "person"

入力された値を複数形or単数形でDBに保存したいときなんかに使えそうです。

attr_accessorがイマイチわからん

こんにちは!kossyです!


さて、今回はattr_accessorについて、そもそも存在意義がわからなかったため、調べてみました。


attr_accessorって?
Progateで少し触りましたが、スライドの説明文には、

情報を持たせるためには、「attr_accessor シンボル」のようにします。
以下の図のようにすることで、Menuクラスのインスタンスに
nameという情報を持たせることができます。
また、この「name」という情報のことを
インスタンス変数と呼びますので覚えておきましょう。

出典:prog-8.com

使い方の説明しか記載されていませんでした。。。






わかりやすい説明文

bryankawa.hatenablog.com
attr_accessorの存在意義がわかりやすく説明されているとのことだったので
読んでみたのですが、
さっぱりわからん
となってしまいました。





getterとsetterについてあまりよく理解できなかったので、
調べてみました。


getterは、

@nameなどのインスタンス変数の値はクラス内からしか取得出来ません。
クラス外からインスタンス変数の値を取得するには、クラス内にdef nameのように定義する
このインスタンス変数の値を取得するためのメソッドを「ゲッター」と呼びます。

あぁ、あの実装はそういう意味があったのか、とすぐに理解はできたのですが、

setterは、def title=(val)
=(val)ってなんだよ、みたいに思ってしまいました。


def メソッド名=(val)については以下の記事が詳しく説明されていました。
bottoms-programming.com

def title=(val) とは

book.title = “test”

と「title」への代入式を書いた時に呼び出されるメソッド。
決して「title=(val)」というメソッドではない。


代入式を書いた時に呼び出されるメソッドだったのですね。


存在意義は

利用用途として、

・オブジェクトの属性を定義したい時
・(gemを作成する時、DBにアクセスする必要がない時)

が以下の記事に紹介されていました。
qiita.com




気をつけよう
RailsばっかりやっているとRubyの理解がおろそかになりがちですね。。。