DB保存時に個人情報的なやつは暗号化しておきたいという要望はよくあるかと思います。
今回はattr_encrypted というgemを利用してみました。
自前で実装するなら ActiveSupport::MessageEncryptor
が利用できるようですのでこちらも少し試してみました。
まずはActiveSupport::MessageEncryptor
以下のような module を作成し、(cipherについては後述)
encrypt_secure_key は secrets.yml に定義。
module Encryptor CIPHER = "aes-256-cbc" def encrypt(password) secure = Rails.application.secrets.encrypt_secure_key crypt = ActiveSupport::MessageEncryptor.new(secure, CIPHER) crypt.encrypt_and_sign(password) end def decrypt(password) secure = Rails.application.secrets.encrypt_secure_key crypt = ActiveSupport::MessageEncryptor.new(secure, CIPHER) crypt.decrypt_and_verify(password) end end
以下のようにmodel側でincludeすれば暗号化できます。復号化もdecrypt呼ぶだけ。
class User < ApplicationRecord include Encryptor before_save :encrypt_collumns def encrypt_collumns self.address = encrypt(self.address) end end
簡単に実装はできましたが、decryptするのが少し面倒。self.address を decryptした項目を返すようにすれば良さそうですが
項目増えるたびにメソッド書くのもなぁという気がします。
次は attr_encrypted
使い方
例えば ssn
という項目を暗号化させたい場合、encrypted_ssn
と encrypted_ssn_iv
を用意します。
create_table :users do |t| t.string :name t.string :encrypted_ssn t.string :encrypted_ssn_iv t.timestamps end
モデル側で以下のようにすればok。簡単に利用できて良いです。
class User attr_encrypted :ssn, key: 'This is a key that is 256 bits!!' end
補足
- 項目のPrefix
encrypted_
は変更可能 - iv について
そもそもivは、暗号化の初期ベクトルのことです。暗号化時には指定されたブロック単位で暗号化して行きますが、前のブロックの暗号化データを排他的論理和で論理演算するという仕組みですが、この初期の暗号化時には前のブロックデータがないので、代わりに与えるのがivとのこと。 - modeについて
mode というオプションがあり、現在デフォルトはper_attribute_iv
というivが必要なものが設定されています。
per_attribute_iv_and_salt
とsingle_iv_and_salt
というものも現在は使用できますが、次回メジャーバージョンアップ時にはなくなる予定とのこと。
以下余談です。
Cipher Suites について
ActiveSupport::MessageEncryptor のデフォルトは aes-256-cbc
でした。
attr_encrypted は aes-256-gcm
です。
ほとんどの人はデフォルトのものを利用するでしょうし、このような用途では通常ivなどの値がhttpのデータとして通信されることはないので、これで良い気がしますが、少し気になったので調べてみます。
使用可能なcipherは以下で確認できます。
require 'openssl' puts OpenSSL::Cipher.ciphers
aes-256だけでも沢山あり、どれ選んでいいかさっぱりわかりません。
aes-256-cbc aes-256-cbc-hmac-sha1 aes-256-cbc-hmac-sha256 aes-256-ccm aes-256-cfb aes-256-cfb1 aes-256-cfb8 aes-256-ctr aes-256-ecb aes-256-gcm aes-256-ofb aes-256-xts
Stack Overflow
Stack Overflow に一部解説がありました。
How to choose an AES encryption mode (CBC ECB CTR OCB CFB)? - Stack Overflow
- ECB should not be used if encrypting more than one block of data with the same key.
同一キーで複数ブロック暗号化するなら使用するな - CBC, OFB and CFB are similar, however OFB/CFB is better because you only need encryption and not decryption, which can save code space.
OFB/CFB は復号化を必要としないなら省スペース。 - CTR is used if you want good parallelization (ie. speed), instead of CBC/OFB/CFB.
CTRは並列化が必要なら利用される。 - XTS mode is the most common if you are encoding a random accessible data (like a hard disk or RAM).
XTSはランダムアクセス時に最も利用される。 - OCB is by far the best mode, as it allows encryption and authentication in a single pass. However there are patents on it in USA.
OCBは効率よく一番いいけど、特許の問題がある。
他にも、uniqueなivが使えないならOCBを使えとあります。
コメントには、GCMはOCBにとても似ており、特許の問題がないので一番良い選択肢だとあります。(ただし実装が複雑)
GCMで良い気がしてきました。
一応ivの生成を確認してみます。
# attr_encrypted-3.0.3/lib/attr_encrypted.rb def generate_iv(algorithm) algo = OpenSSL::Cipher.new(algorithm) algo.encrypt algo.random_iv end
random_iv は以下。
# ruby 2.4.0p0 (2016-12-24 revision 57164) [x86_64-darwin16] def random_iv str = OpenSSL::Random.random_bytes(self.iv_len) self.iv = str end
強度は iv_len で決まりますが、値は12でした。
これが強いのか弱いのか、、、よくわかりません。
("\x00".."\xff").to_a.size => 58
なので、58の12乗ということなのかしら。まぁまぁ大丈夫そう。