1. Kaas59
Changes in body
Source | HTML | Preview
@@ -1,200 +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
+- ユーザの名前:name: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/