Webエンジニア目指して#45

どうもエアコンのカビが辛いマンです。早う一人暮らしがしたいのう... 最近スプラも飽き気味なのでリフレッシュはぷよぷよに移行しつつあります。弥生時代習得したはずなのに反復してなさすぎて抜けちゃった〜と嘆いてたんですが、ぷよスポは先折GTRの時代らしくて助かった

さてやっていきましょうRails XIです。Railsもいよいよラスト

目標

パスワードを文字列そのまま保存しておくと盗まれたら大変なため、盗まれたとしても使い物にならないようハッシュ化する

ハッシュ化用gemのインストール

gemとはrubyの拡張パッケージ。

gemfileの記述

gemfileとは、アプリのルートディレクトリにある、railsにインストールしたいgemを記述するファイル。
f:id:misokatsu_sand:20210501011716p:plain

中身を見てみると、gem 'gem名' , 'バージョン'の形でたくさん並んでいる。
これらはrails newした時に最初にインストールされたものらしい

f:id:misokatsu_sand:20210501012055p:plain バージョンの頭に比較演算子みたいなのがついている。
これには以下のようなルールがある
・ x.0.0 : バージョン固定
・ >= x.0.0 : 演算子通り(x.0.0以上)
・ ~> x.0.0 : x.0.0以上X+x.9.9以下(一桁目固定)
・ '>= x.0.0' , '< x.3.0' : 演算子通りの複合
・ 記述なし : 最新バージョン

さて、今回は使いたいハッシュ化用gem"bcrypt"を記述する。

gem 'bcrypt', '~> 3.1.7'

既にgemfileにコメントアウトしてあった。 これを追加し保存したら、ターミナルでbundle installと入力するとgemのインストールが始まる。

...はずだった。 bundle installすると予期せぬエラーが返ってきた。
gem install 'bcrypt'で切り分けてみたら、permission deniedが返ってきた。 sudoすることで解決できたが、rootでbundler使うなと警告された。

色々調べてみると、過去にどこかしらでsudoでgem入れたことでメインユーザーディレクトリのアクセス権がsuになってたみたい

先人の知恵を借りてchownコマンドで対象ディレクトリのアクセス権を直した。 f:id:misokatsu_sand:20210504153717p:plain

これでgem install 'bcrypt'が成功した。 f:id:misokatsu_sand:20210504154527p:plain 改めてbundle installしてみたが、gem揃ってるぜ!みたいな感じだった

何も知らないままsudoしただろうからどのタイミングでやらかしたか分からないが、system領域でもないのに権限弾かれた時は対象のフォルダ権限を参照してみるのがいいかも

さて解決したところで次

bcryptの使い方

対象モデルクラスにhas_secure_passwordを定義する

has_secure_passwordはpasswordが存在するか自動チェックするため、

passwordカラムに対してのpresence:trueは不要になる。

class User < ApplicationRecord
  has_secure_password
  
  #削除 validates:password,{presence:true}
以下略

password_digestカラムの作成

bcriptはハッシュ化したパスワードをpassword_digestカラムへ保存するため、それ用にmigrationする。passwordカラムは不要になるため削除する

以下のようにremove_columnでカラムの削除ができる

class ChangeUsersColumns < ActiveRecord::Migration[6.1]
  def change
    add_column :users, :password_digest, :string
    remove_column :users, :password, :string
  end
end

password_digestの自動生成

下図のように、passwordに値が与えられるとbcryptによって自動でハッシュ化され
password_digestに代入される。 f:id:misokatsu_sand:20210507034047p:plain

この処理はpasswordカラムがなくても動作する。

そのため、今まで書いたコードでpasswordに代入していた処理をpassword_digestに置換する作業はいらない。 ログイン処理については変更があるので次項で。

ハッシュ化passwordでのログイン

has_secure_passwordを付与したモデルクラスはauthenticateというメソッドが使えるようになる。

authenticateメソッドは@user.authenticate("neko")のように記述し、 引数の文字列をハッシュ化しpassword_digestと一致するかどうかT/Fで判定する。

これを使ってログイン処理を書き替える。

以前書いたログイン処理はemailとpassがそれぞれ入力と一致した@userが存在すればログインする記述だった

def login
    @user=User.find_by(email: params[:email],
                       password: params[:password])
    if @user
以下略

今回はemailが一致する@userが存在し、且つその@userのauthenticateがTrueの場合にログインするようにする。

def login
    @user=User.find_by(email: params[:email])
    if @user && @user.authenticate(params[:password])
以下略

これでログインしてみる。viewは代わり映えしないのでサーバーログを見る f:id:misokatsu_sand:20210507042342p:plain authenticity_token辺りがそれっぽい。ログインは成功

ちなみにgemを入れたらサーバーは再起動しないとこんなエラーが出るみたい f:id:misokatsu_sand:20210507042626p:plain bcryptないわけないじゃん、bundle installでも確認したしと思って一発で再起動思いついたのえらいぞお

bcryptのセキュリティ性の高さは、平文にsalt(ランダムな文字列)を加えて文字数をかさ増ししてからハッシュ化することで総当りに強くなるところらしい。確かにnekoの4文字からでも大変な事になってた

ただ、専門的な突破口はあるみたいなので、お手軽セキュアって感じ?本格的に実装するなら他にもやらないとダメそう

というわけでrails XI終わり。後はrails tutorialとgithubかな
ではでは。