背景
Ruby 2.3.1 -> 2.4.6 へversion upしたら、ActiveSupport::MessageEncryptor周りでエラーがでた。
この中で使われているOpenSSLがもともと32bytes以上でも受け入れていたところを、32バイトぴったりしか受け付けなくなったのが原因の様子。
これが修正のcommit
データベースに32バイトより大きいkeyで暗号化したデータが入っていたので、それらをdecryptできなくて落ちていた。
ちなみにRailsは4系。
捕捉
Rails 5.0.0.1 で対応が入っているらしい
https://github.com/rails/rails/pull/25192/files
でも今回はRailsのversionは上げられないので別の対処法を探す。
色々調べた感じ、多くの人はRubyのversionを戻したりとversionを上げ下げしてこの問題を回避してそうだった。
解決案その1
Ruby 2.3系に戻し、32バイトより大きいkeyでdecryptして、32bytesでまたencryptする。
これでもいいのですが、ちょっと時間がかかりそう。
解決案その2
32バイトっていうので、とりあえず32bytesにしてみた。
しかしdecryptで失敗。
これはchiperの方はsecretを32bytesに切ってくれるが、復号で使うverifierの方はそのままの長さを使ってるからのが原因の模様。
d = ActiveSupport::MessageEncryptor.new(some_key[0..31])
rb(main):005:0> d.encrypt_and_sign("hoge")
=> "K3dtYVJUL1NucVpnRUFJWm9OZ3VQQT09LS1SMlRKb3dWWXFFajZ5MDFMcHQ3NmxRPT0=--910efa18ef525a38f7bf2482e94a54ec39ad1e65"
d = ActiveSupport::MessageEncryptor.new(some_key[0..31])
irb(main):003:0> d.encrypt_and_sign("hoge")
ArgumentError: key must be 32 bytes
しかし、ActiveSupport::MessageEncryptor
のコードを読んでいたら、optionsで渡せそう。
option引数の先頭で渡せば@sign_secret
に入って、そっちが優先されてverifierが作成される。
#=> ActiveSupport::MessageEncryptor
def initialize(secret, *signature_key_or_options)
options = signature_key_or_options.extract_options!
sign_secret = signature_key_or_options.first
@secret = secret
@sign_secret = sign_secret
@cipher = options[:cipher] || 'aes-256-cbc'
@verifier = MessageVerifier.new(@sign_secret || @secret, digest: options[:digest] || 'SHA1', serializer: NullSerializer)
@serializer = options[:serializer] || Marshal
end
つまりこうすれば良い。
ActiveSupport::MessageEncryptor.new(some_key[0..31], some_key)
急ぎだったので、解決案2で解決しました。
salt使うように修正予定。
終わり
ベテランの方に助けてもらいました
ライブラリのコードリーディング力を高めなければ