Fendo181 / RoRTutorial_chap04

0 stars 0 forks source link

第9章 発展的なログイン機構 #34

Open Fendo181 opened 7 years ago

Fendo181 commented 7 years ago

やった事

わかった事

改めてsessionとcookiesの違い。

sessionメソッドで保存した情報は自動的に安全が保たれますが、cookiesメソッドに保存する情報は残念ながらそのようにはなっていません。 sessionメソッドで作成した一時cookiesは自動的に暗号化され、リスト 8.14のコードは保護されます。そしてここが重要なのですが、攻撃者がたとえこの情報をcookiesから盗み出すことができたとしても、それを使って本物のユーザーとしてログインすることはできないのです。 ただし今述べたことは、sessionメソッドで作成した「一時セッション」にしか該当しません。cookiesメソッドで作成した「永続的セッション」ではそこまで断言はできません。永続的なcookiesには、セッションハイジャックという攻撃を受ける可能性が常につきまといます。ユーザーのブラウザ上に保存される情報については、第9章でもう少し注意深く扱うことにします。

remember tokenを使った発展的なログイン機構(remember機能)

rememberメソッドで記憶トークン (remember token) を生成し、cookiesメソッドによる永続的cookiesの作成や、安全性の高い記憶ダイジェスト (remember digest) によるトークン認証にこの記憶トークンを活用します。

手順

実際の処理

 # ランダムなトークンを返す
def User.new_token
  SecureRandom.urlsafe_base64
end

# 永続セッションのためにユーザーをデータベースに記憶する
def remember
  self.remember_token = User.new_token
  update_attribute(:remember_digest, User.digest(remember_token))
end

# 渡されたトークンがダイジェストと一致したらtrueを返す
def authenticated?(remember_token)
  return false if remember_digest.nil?
  BCrypt::Password.new(remember_digest).is_password?(remember_token)
end

permanentメソッド

cookies[:remember_token] = { value:   remember_token,
                             expires: 20.years.from_now.utc }

これは以下のようなpermanentメソッドでも同じ事ができる。

cookies.permanent[:remember_token] = remember_token

signedメソッド

三項演算子

if boolean?
 何かをする
else
 別のことをする
end

# 三項演算子
論理値? ? 何かをする : 別のことをする

テストでユーザがログインするようにする。

ということもできる。 https://github.com/Fendo181/RoRTutorial_chap04/issues/33#issuecomment-313633230

Rememberテスト

テストを忘れている疑いのあるコードブロック内にわざと例外発生(raise)を仕込むというテクニックを使います。つまり、そのコードブロックがテストから漏れていれば、テストはパスしてしまうはずです。

# 記憶トークンcookieに対応するユーザーを返す
def current_user
 #現在ログイン中のユーザを返す(いる場合)
if (user_id = session[:user_id])
  @current_user ||= User.find_by(id: user_id)
elsif (user_id = cookies.signed[:user_id])

  #ここでわざと例外処理を発生させる。
  raise #テストがパスすれば、この部分がテストされちない事がわかる。
 
  user = User.find_by(id: user_id)
  if user && user.authenticated?(cookies[:remember_token])
    log_in user
    @current_user = user
  end
end
end

#永続的セッションを破棄する。
def forget(user)
  user.forget
  cookies.delete(:user_id)
  cookies.delete(:remember_token)
end

current_userメソッドのテスト

(1)fixtureでuser変数を定義する
(2)渡されたユーザーをrememberメソッドで記憶する
(3)current_userが、渡されたユーザーと同じであることを確認します
(4)ユーザーの記憶ダイジェストが記憶トークンと正しく対応していない場合に現在のユーザーがnilになるかどうかをチェックしています

実際のコード

require 'test_helper'

class SessionsHelperTest < ActionView::TestCase
  ##(1)fixtureでuser変数を定義する

  def setup
    #@user = users(:michael)
   ##(2)渡されたユーザーをrememberメソッドで記憶する
    remember(@user)
  end

  ##(3)current_userが、渡されたユーザーと同じであることを確認します

  test 'current_user returns right user when session is nil' do
    assert_equal @user, current_user
    assert is_logged_in?
  end

  ##(4)ユーザーの記憶ダイジェストが記憶トークンと正しく対応していない場合に現在のユーザーがnilになるかどうかをチェックしています

  test 'current_user returns nil when remembe digest is wrong' do
    @user.update_attribute(:remember_digest, User.digest(User.new_token))
    assert_nil current_user
  end
end

assert_equalの引数は「期待する値, 実際の値」の順序で書く点に注意してください。

わからなかった事