パスワードを暗号化して格納

railsでユーザ認証を行うためのモデルを実装してみました。データベース上では、パスワードは平文ではなく、MD5で暗号化したかたちで格納されるよう実装しました。

モデルの生成

railsのgenerateスクリプトでUserモデルを生成します。

% ruby script/generate model User

テーブル定義と生成

生成されたmigrationのソースにテーブルの定義を行います。入力される平文パスワードのカラムは用意せず、暗号化したパスワードのカラムとsaltのカラムを用意します。

class CreateUsers < ActiveRecord::Migration
  def self.up
    create_table :users do |t|
      t.column :name, :string, :limit => 32, :null => false
      t.column :crypted_password, :string, :limit => 32, :null => false
      t.column :salt, :string, :limit => 32, :null => false
      t.column :roll, :integer, :default => 0
      t.timestamps
    end
  end

で、生成。

#rake db:migrate

モデルクラスの定義

Userモデルのクラスを定義します。

    1. DB登録前に自動実行される、フックメソッドであるbefore_createに対して、saltとMD5による暗号化パスワードの生成が行われるよう実装を行います。
    2. それに加えて認証を行うauthoricateメソッドを実装します。このauthoricateメソッドでは、テーブルに格納されている暗号化パスワードとテーブルに格納されているsaltと入力された平文パスワードとからMD5により暗号化した値が等しくなるかを確認します。
    3. DB登録時はこのモデルへのパスワードの入力は平文で行うことになるので、テーブルカラムには存在しないその平文パスワードのためのpassword属性へのアクセサーを定義します。
require 'digest/md5'

class User < ActiveRecord::Base
  attr_accessor :password
  validates_uniqueness_of :name
  validates_presence_of :name, :password
  
  # 認証を行う。
  def authoricate(name, password)
    return false if name != self.name
    return false if User.crypt_password(password, self.salt) != self.crypted_password
    true
  end
  
  # DB格納前のフック
  # saltと暗号化されたパスワードを生成
  def before_create
    self.salt = User.new_salt
    self.crypted_password = User.crypt_password(self.password, self.salt)
  end
  
  private
  
  # パスワードを暗号化する
  def self.crypt_password(password, salt)
    Digest::MD5.hexdigest(password + salt)
  end
  
  # パスワード暗号化のためのsalt生成
  def self.new_salt
    s = rand.to_s.tr('+', '.')
    s[0, if s.size > 32 then 32 else s.size end]
  end
  
end

ユーザを登録する

railsのconsole環境で、ユーザを登録してみます。これは、暗号化しない場合と同様、平文のパスワードをモデルに入力してDBへのsaveを行うことになります。

nyaago@kyonkyon: 580 /Users/nyaago/workspace/cart$./script/console 
Loading development environment (Rails 2.0.2)
>> user = User.new
=> #<User id: nil, name: nil, crypted_password: nil, salt: nil, roll: 0, created_at: nil, updated_at: nil>
>> user.name = "moomin"
=> "moomin"
>> user.password = "moomin"
=> "moomin"
>> user.save
=> true
>> user.save

登録、認証の確認

同じくrailsのconsole環境で、ユーザ名で検索してみてパスワードが暗号化されて登録されているか確認しまてみます。
つづいて、認証メソッドで認証できるか確認しました。登録したパスワードでauthoricateメソッドをコールするとtureが返され、別のパスワードでコールするとfalseが返されることを確認しました。

nyaago@kyonkyon: 583 /Users/nyaago/workspace/cart$./script/console 
Loading development environment (Rails 2.0.2)
>> user = User.find_by_name('moomin')
=> #<User id: 1, name: "moomin", crypted_password: "4e64f0f60ba12cc0084844a78abce52f", salt: "0.709366042469232", roll: 0, created_at: "2008-09-04 15:47:24", updated_at: "2008-09-04 15:47:24">
>> user.authoricate('moomin', 'moomin')
=> true
>> user.authoricate('moomin', 'moomi')
=> false

TODO

このモデルを使用した認証のコントローラーとVIEWを..