投稿日:

DB暗号化を調べてみた

DBに保存する時に個人情報をそのまま保存するのは、万が一DBの情報が流出した時にまずいので、色々と調べてみました。

まず、パスワードはphpのmd5でハッシュ化して保存すれば、復号できないのでOK、パスワードを忘れた場合は、新しいパスワードをランダム生成。
次に、暗号・復号するデータ。
氏名やメールアドレスなど、
php側で暗号・復号する方法と、DB側で暗号・復号する方法があります。(ハードディスク自体暗号の方法もありますが)
で、問題は、LIKE検索とINDEXでした。
基本、暗号化して保存すると上記2点が出来なくなることが問題です。
その点も踏まえてどんな方法があるか調査してみました。
とりあえず今回は
php mysqlの環境で出来ればということで・・・


php側で暗号化・復号

・mcrypt拡張モジュールを使った暗号化
・openssl拡張モジュールを使った暗号化
linuxにパッケージをインストールする必要がある。レンタルサーバーで使用できない場合があるので今回は調査しませんでした。
・PEAR::Crypt_Blowfishを使った暗号化
これはレンタルサーバーでも設置できるので、調査しました。
8バイトごとに暗号化していくみたいで足りない場合は¥0が後ろに足されます。なので、復号時はrtrimで後ろの¥0を取る必要あり。
完全一致検索では、検索でき、INDEXも効きますが、LIKE検索は無理でした。(8バイトごとに暗号化してるので当然ですね・・・)
メールアドレス等の完全一致検索しかしないフィールドであれば有効です。
下記サンプルコード

// Crypt_Blowfish 暗号化キーと初期化ベクトル(8byte文字列)
define('CBF_KEY' , 'abc' );
define('CBF_IV' , '12345678' );
// 暗号化---------------------------
// CBCにて暗号化(キーと初期化ベクトルを引数に与える)
$blowfish = Crypt_Blowfish::factory( 'cbc', CBF_KEY, CBF_IV );
$address = $blowfish->encrypt( $address );
// このままだとバイナリデータなのでbase64で文字列化
$address = base64_encode( $address );
// 復号---------------------------
// 復号したいパスワード
$address = base64_decode( $address); // ←バイナリへ戻す
// CBCにて復号化(キーと初期化ベクトルを引数に与える)
$blowfish = Crypt_Blowfish::factory( 'cbc', CBF_KEY, CBF_IV );
$address = rtrim($blowfish->decrypt( $address ),"\0");
// 検索は暗号化して検索してください。
DB側で暗号化・復号
MySQLのAES_ENCRYPT・AES_DECRYPT
MySQL標準でついている暗号化関数です。
AES_ENCRYPT・AES_DECRYPTだけでは、バイナリになってしまうのでHEXという関数で16進数化して保存します。
LIKE検索が出来ますが、DB内部で復号して検索してるみたいで、INDEXは効きませんでした。
氏名や住所等のフィールドで有効だと思います。
下記サンプルコード

// 「ans_key」は任意の暗号キー
// 登録する時
INSERT INTO users (address) values (HEX(AES_ENCRYPT('大阪市','ans_key')));
// 検索する時
SELECT
*,AES_DECRYPT(UNHEX(address),'ans_key') as ad FROM users
WHERE
AES_DECRYPT(UNHEX(address),'ans_key') LIKE '大阪%';
今回、色々調査してみましたが、LIKE検索、INDEX両方とも使えるのは見つけられませんでした。何かいい方法ないかな?(PEAR::Crypt_Blowfishで文字列1文字ずつやればLIKE検索できるだろうけど、文字数が多くなりすぎるし・・・)