mcrypt関数で暗号化した時のパディングの問題

mcrypt関数でデータを暗号化→復号化した時に、末尾にデータが付加されて元に戻らない場合がある、という事で簡単に実験。


サンプルのテキストファイルを作成する。

$ echo -n 'encrypt-me' > before.txt

中身を確認

$ cat before.txt
encrypt-me


サンプルのテキストファイルをmcrypt関数で暗号化して復号化し、別のファイルへ出力するPHPを作成する。

$text = file_get_contents('before.txt');  // 暗号化したいデータ
$key  = 'secret_key';
$cipher = MCRYPT_RIJNDAEL_128;          // 暗号化アルゴリズム:MCRYPT_BLOWFISH | MCRYPT_3DES | MCRYPT_RIJNDAEL_128 etc...

$encrypt = mcrypt_encrypt($cipher, $key, $text, MCRYPT_MODE_ECB);
$decrypt = mcrypt_decrypt($cipher, $key, $encrypt, MCRYPT_MODE_ECB);

file_put_contents('after.txt', $decrypt);


暗号化→復号化して出力されたファイルを確認

$ cat after.txt
encrypt-me


ファイルサイズを確認

$ ls -l before.txt after.txt
-rw-rw-r-- 1 user user 16  330 19:42 2014 after.txt
-rw-rw-r-- 1 user user 10  330 19:42 2014 before.txt


暗号化前は10バイト、暗号化して復号化したファイルは16バイトになっている。

odコマンドで内容を確認する

$ od -tx1 after.txt
0000000 65 6e 63 72 79 70 74 2d 6d 65 00 00 00 00 00 00
0000020


末尾に、6バイト分の \0(ヌル文字)が増えている。

blowfishやDES、AESなどの暗号を使った場合、データはブロックサイズの倍数に丸められる。

MCRYPT_RIJNDAEL_128 の場合、ブロックサイズは128bit=16Byteなので、データが16Byteの倍数に満たない場合にパディングされる。

パディングには、\0(ヌル文字)が使われる。


上記では、テキストファイルを暗号化/復号化してechoで表示したので、パディングされた事を意識しにくい。

docxなどのファイルだと、うまく開けなくなるので気が付く(docxの場合、ファイルを開く際に警告が表示され、修復すると開く事はできる)。

暗号化して保存した後、それを復号化するとファイルが開けなくなった場合、これが原因の可能性がある。


対策としては、復号化したデータの末尾にある「\0」を削除する。

rtrim関数で削除する文字を指定すると良い。

file_put_contents('after.txt', rtrim($decrypt, "\0"));

ここで第二引数を指定しないと、半角スペースも除去してしまうので注意。


なお、OpenSSL関数を利用すると、上記のようなパディングが自動的に処理されるため、trimする必要が無い。