こんにちは!エイチーム引越し侍の加藤です!
みなさん暗号化してますか?
ユーザの情報とかデータベースに平文で入れてるぜって方は爆速で暗号化してください。
「Rails 暗号化」で検索するとMessageEncryptorに関するQiitaの記事が多く出てくると思うんですが、
この暗号化をそのまま使うと同じ文章でも違う暗号化された文字列になってしまって、
暗号化した文字列をSQLで検索できないじゃまいか!となって困ったのでその解決策を皆さんに共有したいと思います。
Railsの可逆暗号化方法 ActiveSupport::MessageEncryptorの使用方法
こちらでさらーっと書いてあるので楽勝じゃん!と思って継承クラス作ってみる。
1 # ivがランダムだと暗号化した値で検索できないためivを固定化する継承クラス
2 class StaticEncryptor < ::ActiveSupport::MessageEncryptor
3 private
4 def _encrypt(value)
5 cipher = new_cipher
6 cipher.encrypt
7 cipher.key = @secret
8
9 # Rely on OpenSSL for the initialization vector
10 # 検索の為に固定化
11 iv = "hogehogehogehoge" # 16bitじゃないとダメらしい
12
13 cipher.auth_data = "" if aead_mode?
14
15 encrypted_data = cipher.update(@serializer.dump(value))
16 encrypted_data << cipher.final
17
18 blob = "#{::Base64.strict_encode64 encrypted_data}--#{::Base64.strict_encode64 iv}"
19 blob << "--#{::Base64.strict_encode64 cipher.auth_tag}" if aead_mode?
20 blob
21 end
22 end
これで暗号化したらできたぜ!と思うんですが、
復号時にInvalid Message!のエラーが出力されます。
なんでだー!と思って元のクラスをいろいろ見てみると
94 def _encrypt(value)
95 cipher = new_cipher
96 cipher.encrypt
97 cipher.key = @secret
98
99 # Rely on OpenSSL for the initialization vector
100 iv = cipher.random_iv
101 cipher.auth_data = "" if aead_mode?
102
103 encrypted_data = cipher.update(@serializer.dump(value))
104 encrypted_data << cipher.final
105
106 blob = "#{::Base64.strict_encode64 encrypted_data}--#{::Base64.strict_encode64 iv}"
107 blob << "--#{::Base64.strict_encode64 cipher.auth_tag}" if aead_mode?
108 blob
109 end
ん???
100行目固定化するのはいいんだけど、そのあとiv使われてるのって暗号化する文字列として106行目で添えているだけだな…
どこで暗号化に使われているんだろう…
と弊社のエンジニアの神様が鋭い洞察で、
100行目でcipher.random_ivが実行されたときに内部的にivを保持しているはず!と看破!
1 # ivがランダムだと暗号化した値で検索できないためivを固定化する継承クラス
2 class StaticEncryptor < ::ActiveSupport::MessageEncryptor
3 private
4 def _encrypt(value)
5 cipher = new_cipher
6 cipher.encrypt
7 cipher.key = @secret
8
9 # Rely on OpenSSL for the initialization vector
10 # 検索の為に固定化
11 iv = "hogehogehogehoge" # 16bitじゃないとダメらしい
12 cipher.iv = iv
13 cipher.auth_data = "" if aead_mode?
14
15 encrypted_data = cipher.update(@serializer.dump(value))
16 encrypted_data << cipher.final
17
18 blob = "#{::Base64.strict_encode64 encrypted_data}--#{::Base64.strict_encode64 iv}"
19 blob << "--#{::Base64.strict_encode64 cipher.auth_tag}" if aead_mode?
20 blob
21 end
22 end
こうやって12行目でcipherにもivを反映させてあげると復号もうまくいきました。
以上です。
いかがでしたか?
同じ悩みを持っている方もいるんじゃないかなーと思って記事にしてみました。
これでみなさんにハッピーエンジニアライフを送る助力ができたらと思います。
追伸
株式会社エイチーム引越し侍では、一緒にサイト改善をしてくれるWebエンジニアを募集しています。エイチームグループのエンジニアとして働きたい!という方は是非、以下のリンクから応募してください。
皆様からのご応募、お待ちしております!!
神様からの追伸
https://github.com/ruby/openssl/blob/master/ext/openssl/ossl_cipher.c
https://github.com/ruby/openssl/blob/master/lib/openssl/cipher.rb
https://github.com/ruby/openssl/blob/master/ext/openssl/ossl_rand.c