今さらですが、Rails4.1リリースノートにある Message Verifiers を調べてみました.
TL;DR
使い方はとてもシンプルで以下のように使うだけです.
rails_console
> token = Rails.application.message_verifier(:foo).generate('data')
=> "BAhJIglkYXRhBjoGRVQ=--c1d9142a4741bbe8d377d5d9d475948137805e5b"
> Rails.application.message_verifier(:foo).verify(token)
=> "data"
特徴
- config/secrets.ymlの
secret_key_baseと引数(:foo)を使って秘密鍵を作るから強度がある. - データ(
'data')自体は暗号化されてない - Base64の文字列なのでURLに乗せられる
-
+,/,=がメッセージ(token)中に含まれるから少し注意
-
- メッセージが改竄されても検出できる
キーとなるコードとか
-
Rails.application.secrets.secret_key_base
- config/secrets.ymlの
secret_key_base
- config/secrets.ymlの
メッセージのフォーマット
メッセージのフォーマットはデータ格納する[DATA]と
検証で利用する[DIGEST]部分をつないだ文字列になります.
[DATA]--[DIGEST]
試しに以下のメッセージをデコードしていきます.
BAhJIglkYXRhBjoGRVQ=--c1d9142a4741bbe8d377d5d9d475948137805e5b
[DATA]のデコード
MarshalとBase64だけでできてます
[DATA]は以下のような感じでデコードできます
符号化しているだけなので簡単にデコードできます.
::Marshal.load(::Base64.strict_decode64('BAhJIglkYXRhBjoGRVQ='))
# => "data"
[DIGEST]のデコード
Rails.application.key_generatorを使った秘密鍵と
HMCAのハッシュ値を計算しています.
secret = Rails.application.key_generator.generate_key('foo')
OpenSSL::HMAC.hexdigest(OpenSSL::Digest.const_get('SHA1').new, secret, 'BAhJIglkYXRhBjoGRVQ=')
# => "c1d9142a4741bbe8d377d5d9d475948137805e5b"
検証自体はこのハッシュ値とメッセージに含まれるハッシュ値を比較してる感じです.
正確には単純な文字列比較でなくバイナリ単位で比較してるっぽい.