attr_encryptedされたカラムに対してwhere likeしたかった。
鶏の骨を食べていたところ、歯の詰め物が砕けました。保険があるうちでよかったですね。
MySQL側で復号することで解決できました。
Ruby - MySQLのAES_ENCRYPT/AES_DECRYPT互換の方式でActiveRecordの属性を透過的に暗号化/復号する - Qiita この記事にしたがった上で
scope :email, ->(search) { where("AES_DECRYPT(encrypted_email, 'KEY_STR') like ?", "%#{search}%") }
でいけました。ほんとうに感謝。
以下顛末。
attr_encryptedが提供してくれるfind_by_hogeは完全一致しかみてくれない
class Sec < ActiveRecord::Base
attr_encrypted :email,
key: 'KEY_STR'
end
class CreateSecs < ActiveRecord::Migration
def change
create_table :secs do |t|
t.binary :encrypted_email
end
end
end
があるとして
Sec.create!(email: 'akane@example.com')
Sec.find_by_email('akane')
Sec Load (6.0ms) SELECT `secs`.* FROM `secs` WHERE `secs`.`encrypted_email` = x'696f4a636665336a57444e536f794d49736e437453413d3d0a' LIMIT 1
=> nil
そうでしょうとも。
普通のwhereはできない
Sec.where('email like ?', 'akane')
Sec Load (0.5ms) SELECT `secs`.* FROM `secs` WHERE (email like 'akane')
Mysql2::Error: Unknown column 'email' in 'where clause': SELECT `secs`.* FROM `secs` WHERE (email like 'akane')
=> #<Sec::ActiveRecord_Relation:0x3fe4f1990c80>
:pensive:
MySQLと暗号化復号手段を合わせ、whereするまえにMySQL側で復号する
Ruby - MySQLのAES_ENCRYPT/AES_DECRYPT互換の方式でActiveRecordの属性を透過的に暗号化/復号する - Qiita この記事にならい、MySQLと暗号化複号の手段を合わせ、where時以外には何も意識せずに済むようにします。
class Sec < ActiveRecord::Base
attr_encrypted :email,
:algorithm => "aes-128-ecb",
:iv => "",
:key => :generate_key,
:encode => false
def generate_key()
key = "KEY_STR"
final_key = "\0" * 16
key.length.times do |i|
final_key[i % 16] = (final_key[i % 16].ord ^ key[i].ord).chr
end
final_key
end
end
そしてwhere like
においては対象のカラムをMySQL側で復号したものを対象とします。
scope :email, ->(search) { where("AES_DECRYPT(encrypted_email, 'KEY_STR') like ?", "%#{search}%") }
5000件ダミーをぶちこんだDBでゴー。
Sec.email('akane').map(&:email)
Sec Load (0.3ms) SELECT `secs`.* FROM `secs` WHERE (AES_DECRYPT(encrypted_email, 'KEY_STR') like '%akane%')
=> ["akane@example.com",
"ootsuka_akane@example.com",
"okabe_akane@example.com",
"kitakawa_takane@example.com",
"matsuoka_takane@example.com",
"nakane_ryou@example.com",
"nakane_katsuhisa@example.com",
"morioka_takane@example.com",
"nakane_ryouko@example.com",
"iwata_akane@example.com",
"ooya_akane@example.com",
"shimoda_takane@example.com",
"tannba_takane@example.com",
"tsushima_takane@example.com",
"uematsu_akane@example.com",
"kuwata_takane@example.com",
"terao_takane@example.com",
"sawajiri_akane@example.com",
"murata_akane@example.com",
"ookuma_takane@example.com",
"mimura_akane@example.com",
"matsuda_akane@example.com",
"kawabata_takane@example.com"]
幸せになった。
ところで暗号化っているの?
自分が携わったプロジェクトではすべてが一つのサーバーで完結するものであり、SQLサーバーは外部からの接続を許していませんでした。そんなSQLサーバーにアクセスされた時点でもう試合終わってるんじゃないですか感があった。
なおかつwhere
とか自由にならなかったんで、本当に暗号化が必要なのか疑問だったので質問を社の人間に投げたところ、結局社の最高責任者からも明確な回答は得られず、matzに聞けで聞けとか言い出す始末(どんな会社やねん思われるわ)。
暗号化するしないの分岐点がわかる資料などございましたらご教示ください。