はじめに
フォームを介してユーザが入力したパスワードをそのままDBに保存するのは危険なので、暗号化しますよね。パスワードを適切に暗号化することで、第三者に、DBからパスワードをコピー盗まれないようにできます。
ということで、今日は、Rails のモデル(フォーム)でパスワードを暗号化して保存する方法を紹介します。
環境
この記事では以下の環境(2018年6月12日時点)で動作確認できました。
- Ruby: 2.4.1
- Rails: 5.0.7
- bcrypt: 3.1.7
bcrypt
は 今回使用するGem
です。
導入方法
テーブル設計
今回は仮で、よくありそうな、ユーザー情報を登録するUser
モデルを作成していきましょう。
$ rails g model User name:string email:string password_digest:string
ポイント1:モデルにpassword_digest属性を追加する
このコマンドで生成されるマイグレーションファルは、以下の感じです。
class CreateUsers < ActiveRecord::Migration[5.2]
def change
create_table :users do |t|
t.string :name
t.string :email
t.string :password_digest
t.timestamps
end
end
end
マイグレーションを実行することで、users
テーブルを作成します。
$ rails db:migrate
これで、users
テーブルが作成されました。
mysql> describe users;
+-----------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------------+--------------+------+-----+---------+----------------+
| id | bigint(20) | NO | PRI | NULL | auto_increment |
| name | varchar(255) | YES | | NULL | |
| email | varchar(255) | YES | | NULL | |
| password_digest | varchar(255) | YES | | NULL | |
| created_at | datetime | NO | | NULL | |
| updated_at | datetime | NO | | NULL | |
+-----------------+--------------+------+-----+---------+----------------+
できたカラムは
- ユーザの名前:email:string
- ユーザのメールアドレス:email:string
- ログイン認証のためのユーザパスワード(暗号化される):password_digest:string
です。
password_digest
とは
暗号の分野では、暗号化(ダイジェスト)された文字列をメッセージダイジェストと呼びます。メッセージダイジェストは、元の文字列に戻すことができません。
has_secure_password
ではデータベース内のpassword_digest
という属性に保存できるようになります。(password
ではない点に注意してください)
モデル
ポイント2:$ rails g model ...
で生成されたモデルファイルにhas_secure_password を記述します
class User < ApplicationRecord
has_secure_password
end
こうするだけで、下記の暗号化に関する便利な機能が提供されます。
- users テーブルにパスワードを保存するとき、パスワードを暗号化して保存してくれる
- ログイン用のフォーム(
View
)にpassword
とpassword_confirmation
という変数をモデルに追加すれば暗号化してDBに保存してくれる - ログイン認証用のメソッドである
authenticate
が使用できる
簡単に言えば、password_digest
カラムを用意して、モデルファイルにhas_secure_password
を記述すれば、ログインの機能をよしなにやってくれるやつです。
bcrypt
のGem
を追加
暗号化のためにはbcrypt
というGem
が必要です。
ポイント3:現在、Gemfile
では、bcrypt
がコメントアウトされているので外しましょう
...
# Use ActiveModel has_secure_password
gem 'bcrypt', '~> 3.1.7'
...
Gemfile
が更新できたので、$ bundle install
してbcrypt
をインストールします。bundle install
した後は、サーバを再起動しないとGem
がうまく読み込まれないのでやっておきましょう。
rails コンソールでユーザーを作成
User
インスタンスを作成
¥2.4.1 :001 > user = User.new(name: 'taro', email: 'taro@gmail.com', password: 'taro')
(3.1ms) SET NAMES utf8, @@SESSION.sql_mode = CONCAT(CONCAT(@@sql_mode, ',STRICT_ALL_TABLES'), ',NO_AUTO_VALUE_ON_ZERO'), @@SESSION.sql_auto_is_null = 0, @@SESSION.wait_timeout = 2147483
=> #<User id: nil, name: "taro", email: "taro@gmail.com", password_digest: "$2a$10$1gpw0o4ZTEUoTfk3bOmmJeMKqiz2HPpZ4jACHJfsDw5...", created_at: nil, updated_at: nil>
保存します
2.4.1 :002 > user.save
(0.1ms) BEGIN
User Exists (0.3ms) SELECT 1 AS one FROM `users` WHERE `users`.`email` = 'taro@gmail.com' LIMIT 1
User Create (0.2ms) INSERT INTO `users` (`name`, `email`, `password_digest`, `created_at`, `updated_at`) VALUES ('taro', 'taro@gmail.com', '$2a$10$1gpw0o4ZTEUoTfk3bOmmJeMKqiz2HPpZ4jACHJfsDw5Ad839Tp.xu', '2018-06-13 14:20:55', '2018-06-13 14:20:55')
(1.7ms) COMMIT
=> true
保存できました。確認して見ましょう。
2.4.1 :003 > User.last
User Load (0.3ms) SELECT `users`.* FROM `users` ORDER BY `users`.`id` DESC LIMIT 1
=> #<User id: 1, name: "taro", email: "taro@gmail.com", password_digest: "$2a$10$1gpw0o4ZTEUoTfk3bOmmJeMKqiz2HPpZ4jACHJfsDw5...", created_at: "2018-06-13 14:20:55", updated_at: "2018-06-13 14:20:55">
最後に保存したものを出す方法で、さっき保存したものが出力されました。
password_digest
カラムに入っているデータがダイジェスト(暗号化)されているとわかります。
has_secure_password
によって提供されるログイン認証メソッドであるauthenticate
も確認しておきます。
2.4.1 :004 > user = User.last
User Load (0.4ms) SELECT `users`.* FROM `users` ORDER BY `users`.`id` DESC LIMIT 1
=> #<User id: 1, name: "taro", email: "taro@gmail.com", password_digest: "$2a$10$1gpw0o4ZTEUoTfk3bOmmJeMKqiz2HPpZ4jACHJfsDw5...", created_at: "2018-06-13 14:20:55", updated_at: "2018-06-13 14:20:55">
2.4.1 :005 > user.authenticate('tarachang')
=> false
2.4.1 :006 > user.authenticate('taro')
=> #<User id: 1, name: "taro", email: "taro@gmail.com", password_digest: "$2a$10$1gpw0o4ZTEUoTfk3bOmmJeMKqiz2HPpZ4jACHJfsDw5...", created_at: "2018-06-13 14:20:55", updated_at: "2018-06-13 14:20:55">
正しいパスワード を入力すると、該当するユーザーのインスタンスが返されました。
authenticateメソッドとは
authenticateメソッドは、パスワードを認証するためのメソッドです。試しに使って見ます。
@angou.authenticate("test")
もし、@angou
(モデルのオブジェクト)のパスワードがtest
ならtrue
が返ってきます。
暗号化の実装手順はこんな感じです!最後におさらい!
暗号化のポイントのおさらい
- モデル(マイグレーションファイル)に追加するカラム名は
password_digest
とする -
has_secure_password
をモデルファイルに記述する -
bcrypt
というgem
をインストールする
以上です!
この記事を読んだ方に
この記事を読んで、誤っている箇所をみつけたり、追記した方がいい内容などありましたら、編集リクエストやコメント欄で指摘していただけると助かります。