ActiveSupport::MessageEncryptorを慎重に使う
経緯
Rubyを2.3系から2.4.1に上げたら、ActiveSupport::MessageEncryptorを使っているところが壊れた。ちなみにRailsは5.0.1。
key must be 32 bytes /Users/nisshiee/.rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/activesupport-5.0.1/lib/active_support/message_encryptor.rb:79:in `key=' /Users/nisshiee/.rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/activesupport-5.0.1/lib/active_support/message_encryptor.rb:79:in `_encrypt' /Users/nisshiee/.rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/activesupport-5.0.1/lib/active_support/message_encryptor.rb:60:in `encrypt_and_sign' ...
起こっていたこと
RailsではなくRubyの方のopenssl
パッケージが変更されていた。
これまではkey
に長過ぎる値を渡した場合は勝手に正しい長さに切り取ってくれたが、「想定外の結果(unexpected encryption/decryption results)」を引き起こすとして、正しい長さのkey
を渡さないと例外がraiseされるようになった。
これを受けて、Rails側も、いくつかのデフォルト引数の値が見直された。この変更は v5.1.0.rc1
には入っているので、5.1.0のときには適用されていると思われる。
ただし、この変更は「長過ぎるkeyのトリミングは代わりにRailsが担いますよ」というものではないので、例えば以下のようなコードを書いている場合は引き続き例外がraiseされると思われる(未検証)。
ActiveSupport::MessageEncryptor.new("<長過ぎるkey>")
どう書くべきか
結論から書くと、こんな感じが良いと思う。
ENCRYPT_CIPHER = 'aes-256-cbc' ENCRYPTOR = begin key_len = ActiveSupport::MessageEncryptor .key_len(ENCRYPT_CIPHER) key = ActiveSupport::KeyGenerator .new("<環境から挿入したkey>") .generate_key("<環境から挿入したsalt>", key_len) ActiveSupport::MessageEncryptor.new( key, cipher: ENCRYPT_CIPHER, digest: 'SHA1', serializer: Marshal, ) end
方針としては、
- デフォルト値には頼らない(updateで値を変えられちゃたまらん)
- keyは
ActiveSupport::KeyGenerator
で生成する
という感じ。とにかく暗号化して保存しておいたものが、突然復号化できなくなるという悪夢は避けたい。
ちなみに、動かすだけなら
ActiveSupport::MessageEncryptor.new("<ピッタリ32byteになる文字列>")
でも動くのだが、
- デフォルト引数に頼っているのでデフォルト値の変更には弱い
- keyを文字列で記載しているので、keyの32byte分の情報量を使い切れておらず、ちょっと暗号が弱くなってる(・・・かも。専門家じゃないのでただの予想)
と思われるので、ActiveSupport::KeyGenerator
を使って、長めのkey文字列から32byteのkeyを生成したほうが良さそう。
※ 本記事の内容は予測も含まれるので、指摘大歓迎