こんにちは、鈴木です。
Rails 4.1.0 では Message Verifier という機能が追加されました。
Rails は Cookie に設定する値に HMAC での署名を付けており、ブラウザから受け取った Cookie の内容が改変されていないか検証しています。ここで使用されている機能が Message Verifier であり、Rails 4.1.0 ではそれを再利用できるようになりました。
とりあえず使ってみる
Message Verifier は Rails.application.message_verifier を通じて取得することができます。
引数には用途を表す値を指定します。
1 |
verifier = Rails.application.message_verifier(:greeting) |
verifier の generate メソッドで書名付きメッセージを生成することができます。
1 |
signed_message = verifier.generate('Hello') |
書名付きメッセージは Cookie に保存するとか、パラメータ付きの URL に含めるなどして、システム外に公開することになります。
システム外に公開するということは、その値が改変されてしまう可能性があります。そこで verifier の verify メソッドで改変されていないか検証します。
1 |
message = verify(token) # => "Hello" |
検証に失敗した場合は ActiveSupport::MessageVerifier::InvalidSignature が raise されます。
Message Verifier の使い道を考える
Rails は Cookie の値が改変されていないか検証するために Message Verifier の機能を使っています。
他の使い道を考えてみましたが、よくある会員サイトの友達紹介機能に使えるかもしれません。以下のような良くあるアレです。
- 招待したい友達のメールアドレスを入力して送信ボタンを押す。
- 友達のメールアドレスに専用の URL が記載されたメールが送信される。(URL のパラメータにはトークンが含まれる)
- その URL から会員登録すると友達と招待した人にポイントが付与される。
Message Verifier を使うのは、専用の URL パラメータに含めるトークンを生成する部分と、アクセスされた URL から取り出したトークンを検証する部分です。
全てのコードを書くと長くなるので、モデルクラス(User)のコードだけ書いてみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
class User < ActiveRecord::Base # トークンを生成する. def generate_invitation_token verifier = Rails.application.message_verifier(:invite_friend) verifier.generate([self.id, Time.now]) end # トークンから紹介者を検索する. def self.find_by_invitation_token(token) verifier = Rails.application.message_verifier(:invite_friend) id, invited_at = verifier.verify(token) raise 'Token expired' if invited_at < 1.days.ago # 1 日以上経過している場合はエラーとする. find(id) end end |
generate_invitation_token(email) メソッドでは専用の URL に含めるトークンを生成しています。
find_by_invitation_token(token) メソッドは専用の URL に含めたトークンから招待したユーザを検索します。トークンが改変されていた場合は ActiveSupport::MessageVerifier::InvalidSignature が raise されます。また、トークンが生成されてから 1 日以上経過していた場合も例外を raise しています。
Message Verifier は値を失効させる(有効期限切れにする)機能は提供しない
Message Verifier を使うときには、以下の点に注意しましょう。
- 値の改変を検出する機能を提供してくれる。
- 生成した値を失効させる(有効期限切れにする)機能は提供してくれない。
上記の「よくある会員サイトの友達紹介機能」の例で、以下のように操作された場合にどうなるでしょうか。
- 招待したい友達のメールアドレスを入力して送信ボタンを押す。
- 友達のメールアドレスに専用の URL が記載されたメールが送信される。(URL のパラメータにはトークンが含まれる)
- その URL から会員登録すると友達と招待した人にポイントが付与される。
- もう一度同じ URL にアクセスする。
できれば画面には「その URL は既に無効です」のようなメッセージが表示されて欲しいです。
別のパターンとして、
- 招待したい友達のメールアドレスを入力して送信ボタンを押す。
- 友達のメールアドレスに専用の URL が記載されたメールが送信される。(URL のパラメータにはトークンが含まれる)
- その URL から会員登録すると友達と招待した人にポイントが付与される。
- URL が流出して、本来アクセスして欲しい人とは別の人がアクセスしてきた。
という場合はどうでしょう。
このような問題を防ぐには、次のような対策が必要です。
- 生成するトークンに招待した友達のメールアドレスも含めて、メールアドレスのチェックも行う。
- 同じメールアドレスで既に登録されている場合には「その URL は既に無効です」などのメッセージを表示する。
Message Verifier を使うと値をシステム側(DB など)に保存する必要が無いというメリットがありますが、このような問題が起こりえるということを考慮した上で活用しましょう。