Help us understand the problem. What is going on with this article?

RubyonRailsのActiveRecordで情報を暗号化してSQLで検索もできるようにする方法(ActiveSupport::MessageEncryptor)

More than 1 year has passed since last update.

こんにちは!エイチーム引越し侍の加藤です!

みなさん暗号化してますか?

ユーザの情報とかデータベースに平文で入れてるぜって方は爆速で暗号化してください。
「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エンジニアを募集しています。エイチームグループのエンジニアとして働きたい!という方は是非、以下のリンクから応募してください。
皆様からのご応募、お待ちしております!!

神様からの追伸

image.png
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

エイチームグループ採用サイト(Web開発エンジニア職)

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした