1. ryosuketter

    Posted

    ryosuketter
Changes in title
+Rails のモデル(フォーム)でパスワードを暗号化して保存する方法
Changes in tags
Changes in body
Source | HTML | Preview
@@ -0,0 +1,200 @@
+# はじめに
+
+フォームを介してユーザが入力したパスワードをそのままDBに保存するのは危険なので、暗号化しますよね。パスワードを適切に暗号化することで、第三者に、DBからパスワードをコピー盗まれないようにできます。
+
+ということで、今日は、Rails のモデル(フォーム)でパスワードを暗号化して保存する方法を紹介します。
+
+# 環境
+
+この記事では以下の環境(2018年6月12日時点)で動作確認できました。
+
+- Ruby: 2.4.1
+- Rails: 5.0.7
+- bcrypt: 3.1.7
+
+`bcrypt`は 今回使用する`Gem`です。
+
+# 導入方法
+
+## テーブル設計
+
+今回は仮で、よくありそうな、ユーザー情報を登録する`User`モデルを作成していきましょう。
+
+```bash
+$ rails g model User name:string email:string password_digest:string
+```
+
+ポイント1:**モデルにpassword_digest属性を追加する**
+
+このコマンドで生成されるマイグレーションファルは、以下の感じです。
+
+```ruby: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`テーブルを作成します。
+
+```bash
+$ rails db:migrate
+```
+
+これで、`users`テーブルが作成されました。
+
+```sql
+
+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 を記述します**
+
+```ruby:app/models/user.rb
+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`がコメントアウトされているので外しましょう**
+
+```ruby:Gemfile
+...
+
+# Use ActiveModel has_secure_password
+gem 'bcrypt', '~> 3.1.7'
+
+...
+```
+
+`Gemfile`が更新できたので、`$ bundle install`して`bcrypt`をインストールします。`bundle install`した後は、サーバを再起動しないと`Gem`がうまく読み込まれないのでやっておきましょう。
+
+# rails コンソールでユーザーを作成
+
+`User`インスタンスを作成
+
+```console
+¥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>
+```
+
+保存します
+
+```bash
+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
+```
+
+保存できました。確認して見ましょう。
+
+```bash
+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`も確認しておきます。
+
+```bash
+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メソッドは、パスワードを認証するためのメソッドです。試しに使って見ます。
+
+```ruby
+@angou.authenticate("test")
+```
+
+もし、`@angou`(モデルのオブジェクト)のパスワードが`test`なら`true`が返ってきます。
+
+暗号化の実装手順はこんな感じです!最後におさらい!
+
+# 暗号化のポイントのおさらい
+
+- モデル(マイグレーションファイル)に追加するカラム名は`password_digest`とする
+- `has_secure_password`をモデルファイルに記述する
+- `bcrypt`という`gem`をインストールする
+
+---
+
+以上です!
+
+# この記事を読んだ方に
+
+この記事を読んで、誤っている箇所をみつけたり、追記した方がいい内容などありましたら、編集リクエストやコメント欄で指摘していただけると助かります。
+
+
+
+# 参考
+- [Railsチュートリアル / 暗号化されたパスワード](https://railstutorial.jp/chapters/modeling-users?version=4.0#sec-an_encrypted_password)
+- https://qiita.com/tatane616/items/c00182179e498aa9c53e
+- https://qiita.com/chobi9999/items/20b962a324a0bdbfc0dc
+- https://qiita.com/hmuronaka/items/f2107e8ad84d842ff476
+- https://codezine.jp/article/detail/10014
+- http://rice-american.github.io/blog/2014/03/03/secdb-rails/