Webエンジニア目指して#45
どうもエアコンのカビが辛いマンです。早う一人暮らしがしたいのう... 最近スプラも飽き気味なのでリフレッシュはぷよぷよに移行しつつあります。弥生時代習得したはずなのに反復してなさすぎて抜けちゃった〜と嘆いてたんですが、ぷよスポは先折GTRの時代らしくて助かった
さてやっていきましょうRails XIです。Railsもいよいよラスト
目標
パスワードを文字列そのまま保存しておくと盗まれたら大変なため、盗まれたとしても使い物にならないようハッシュ化する
ハッシュ化用gemのインストール
gemとはrubyの拡張パッケージ。
gemfileの記述
gemfileとは、アプリのルートディレクトリにある、railsにインストールしたいgemを記述するファイル。
中身を見てみると、gem 'gem名' , 'バージョン'
の形でたくさん並んでいる。
これらはrails new
した時に最初にインストールされたものらしい
バージョンの頭に比較演算子みたいなのがついている。
これには以下のようなルールがある
・ 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コマンドで対象ディレクトリのアクセス権を直した。
これでgem install 'bcrypt'
が成功した。
改めて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に代入される。
この処理は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は代わり映えしないのでサーバーログを見る authenticity_token辺りがそれっぽい。ログインは成功
ちなみにgemを入れたらサーバーは再起動しないとこんなエラーが出るみたい bcryptないわけないじゃん、bundle installでも確認したしと思って一発で再起動思いついたのえらいぞお
bcryptのセキュリティ性の高さは、平文にsalt(ランダムな文字列)を加えて文字数をかさ増ししてからハッシュ化することで総当りに強くなるところらしい。確かにnekoの4文字からでも大変な事になってた
ただ、専門的な突破口はあるみたいなので、お手軽セキュアって感じ?本格的に実装するなら他にもやらないとダメそう