Help us understand the problem. What is going on with this article?

Rails のモデル(フォーム)でパスワードを暗号化して保存する方法

More than 1 year has passed since last update.

はじめに

フォームを介してユーザが入力したパスワードをそのまま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属性を追加する

このコマンドで生成されるマイグレーションファルは、以下の感じです。

20180613094857_create_users.rb
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とは

暗号の分野では、暗号化(ダイジェスト)された文字列をメッセージダイジェストと呼びます。メッセージダイジェストは、元の文字列に戻すことができません。

http://wa3.i-3-i.info/word15954.html

has_secure_passwordではデータベース内のpassword_digestという属性に保存できるようになります。(passwordではない点に注意してください)

モデル

ポイント2:$ rails g model ...で生成されたモデルファイルにhas_secure_password を記述します

app/models/user.rb
class User < ApplicationRecord
  has_secure_password
end

こうするだけで、下記の暗号化に関する便利な機能が提供されます。

  • users テーブルにパスワードを保存するとき、パスワードを暗号化して保存してくれる
  • ログイン用のフォーム(View)にpasswordpassword_confirmationという変数をモデルに追加すれば暗号化してDBに保存してくれる
  • ログイン認証用のメソッドであるauthenticateが使用できる

簡単に言えば、password_digestカラムを用意して、モデルファイルにhas_secure_passwordを記述すれば、ログインの機能をよしなにやってくれるやつです。

bcryptGemを追加

暗号化のためにはbcryptというGemが必要です。

ポイント3:現在、Gemfileでは、bcryptがコメントアウトされているので外しましょう

Gemfile
...

# 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をインストールする

以上です!

この記事を読んだ方に

この記事を読んで、誤っている箇所をみつけたり、追記した方がいい内容などありましたら、編集リクエストやコメント欄で指摘していただけると助かります。

参考

ryosuketter
偏差値50台 大学理系院卒、一部上場企業でWebエンジニア(3年目)半人前レベル。普通の34歳おっさんでも頑張ったら一人前のフロントエンドエンジニアになれるのか?の検証 Qiita です。
https://note.com/ryosuketter
life-a-tm
人生のイベントや日常生活に密着した比較サイト、情報サイト等様々なウェブサービスを企画・開発・運営
https://life.a-tm.co.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away