暗号化フィルタ
暗号化フィルタは、ファイル/ストリーム を暗号化する場合に特に有用です。
mcrypt.* と mdecrypt.*
警告
この機能は PHP 7.1.0 で 非推奨になります。この機能に頼らないことを強く推奨します。
mcrypt.*
と mdecrypt.*
は、libmcrypt を用いた暗号化・復号化を行います。これらのフィルタは、
mcrypt 暗号化関数 で利用可能な
アルゴリズムをサポートしており、mcrypt.ciphername
という名前で利用できます。ciphername
の部分は、
mcrypt_module_open() に渡すのと同じアルゴリズム名
です。また、以下の 5 つのパラメータが利用できます。
パラメータ名 | 必須? | 初期値 | 値の例 |
---|---|---|---|
mode | 任意 | cbc | cbc, cfb, ecb, nofb, ofb, stream |
algorithms_dir | 任意 | ini_get('mcrypt.algorithms_dir') | アルゴリズムモジュールのある場所 |
modes_dir | 任意 | ini_get('mcrypt.modes_dir') | モードモジュールのある場所 |
iv | 必須 | N/A | 通常は 8, 16, あるいは 32 バイトのバイナリデータ。暗号の形式に依存する |
key | 必須 | N/A | 通常は 8, 16, あるいは 32 バイトのバイナリデータ。暗号の形式に依存する |
例1 Blowfish を用いた 暗号化/復号化
<?php
//$key は、ここより以前で生成されているものとする
$iv_size = mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_CBC);
$iv = mcrypt_create_iv($iv_size, MCRYPT_DEV_URANDOM);
$fp = fopen('encrypted-file.enc', 'wb');
fwrite($fp, $iv);
$opts = array('mode'=>'cbc','iv'=>$iv, 'key'=>$key);
stream_filter_append($fp, 'mcrypt.blowfish', STREAM_FILTER_WRITE, $opts);
fwrite($fp, 'message to encrypt');
fclose($fp);
// 復号化する...
$fp = fopen('encrypted-file.enc', 'rb');
$iv = fread($fp, $iv_size = mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_CBC));
$opts = array('mode'=>'cbc','iv'=>$iv, 'key'=>$key)
stream_filter_append($fp, 'mdecrypt.blowfish', STREAM_FILTER_READ, $opts);
$data = rtrim(stream_get_contents($fp));//trims off null padding
fclose($fp);
echo $data;
?>
例2 ファイルを AES-128 CBC と SHA256 HMAC で暗号化する
<?php
AES_CBC::encryptFile($password, "plaintext.txt", "encrypted.enc");
AES_CBC::decryptFile($password, "encrypted.enc", "decrypted.txt");
class AES_CBC
{
protected static $KEY_SIZES = array('AES-128'=>16,'AES-192'=>24,'AES-256'=>32);
protected static function key_size() { return self::$KEY_SIZES['AES-128']; } //default AES-128
public static function encryptFile($password, $input_stream, $aes_filename){
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$fin = fopen($input_stream, "rb");
$fc = fopen($aes_filename, "wb+");
if (!empty($fin) && !empty($fc)) {
fwrite($fc, str_repeat("_", 32) );//placeholder, SHA256 HMAC will go here later
fwrite($fc, $hmac_salt = mcrypt_create_iv($iv_size, MCRYPT_DEV_URANDOM));
fwrite($fc, $esalt = mcrypt_create_iv($iv_size, MCRYPT_DEV_URANDOM));
fwrite($fc, $iv = mcrypt_create_iv($iv_size, MCRYPT_DEV_URANDOM));
$ekey = hash_pbkdf2("sha256", $password, $esalt, $it=1000, self::key_size(), $raw=true);
$opts = array('mode'=>'cbc', 'iv'=>$iv, 'key'=>$ekey);
stream_filter_append($fc, 'mcrypt.rijndael-128', STREAM_FILTER_WRITE, $opts);
$infilesize = 0;
while (!feof($fin)) {
$block = fread($fin, 8192);
$infilesize+=strlen($block);
fwrite($fc, $block);
}
$block_size = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$padding = $block_size - ($infilesize % $block_size);//$padding is a number from 1-16
fwrite($fc, str_repeat(chr($padding), $padding) );//perform PKCS7 padding
fclose($fin);
fclose($fc);
$hmac_raw = self::calculate_hmac_after_32bytes($password, $hmac_salt, $aes_filename);
$fc = fopen($aes_filename, "rb+");
fwrite($fc, $hmac_raw);//overwrite placeholder
fclose($fc);
}
}
public static function decryptFile($password, $aes_filename, $out_stream) {
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$hmac_raw = file_get_contents($aes_filename, false, NULL, 0, 32);
$hmac_salt = file_get_contents($aes_filename, false, NULL, 32, $iv_size);
$hmac_calc = self::calculate_hmac_after_32bytes($password, $hmac_salt, $aes_filename);
$fc = fopen($aes_filename, "rb");
$fout = fopen($out_stream, 'wb');
if (!empty($fout) && !empty($fc) && self::hash_equals($hmac_raw,$hmac_calc)) {
fread($fc, 32+$iv_size);//skip sha256 hmac and salt
$esalt = fread($fc, $iv_size);
$iv = fread($fc, $iv_size);
$ekey = hash_pbkdf2("sha256", $password, $esalt, $it=1000, self::key_size(), $raw=true);
$opts = array('mode'=>'cbc', 'iv'=>$iv, 'key'=>$ekey);
stream_filter_append($fc, 'mdecrypt.rijndael-128', STREAM_FILTER_READ, $opts);
while (!feof($fc)) {
$block = fread($fc, 8192);
if (feof($fc)) {
$padding = ord($block[strlen($block) - 1]);//assume PKCS7 padding
$block = substr($block, 0, 0-$padding);
}
fwrite($fout, $block);
}
fclose($fout);
fclose($fc);
}
}
private static function hash_equals($str1, $str2) {
if(strlen($str1) == strlen($str2)) {
$res = $str1 ^ $str2;
for($ret=0,$i = strlen($res) - 1; $i >= 0; $i--) $ret |= ord($res[$i]);
return !$ret;
}
return false;
}
private static function calculate_hmac_after_32bytes($password, $hsalt, $filename) {
static $init=0;
$init or $init = stream_filter_register("user-filter.skipfirst32bytes", "FileSkip32Bytes");
$stream = 'php://filter/read=user-filter.skipfirst32bytes/resource=' . $filename;
$hkey = hash_pbkdf2("sha256", $password, $hsalt, $iterations=1000, 24, $raw=true);
return hash_hmac_file('sha256', $stream, $hkey, $raw=true);
}
}
class FileSkip32Bytes extends php_user_filter
{
private $skipped=0;
function filter($in, $out, &$consumed, $closing) {
while ($bucket = stream_bucket_make_writeable($in)) {
$outlen = $bucket->datalen;
if ($this->skipped<32){
$outlen = min($bucket->datalen,32-$this->skipped);
$bucket->data = substr($bucket->data, $outlen);
$bucket->datalen = $bucket->datalen-$outlen;
$this->skipped+=$outlen;
}
$consumed += $outlen;
stream_bucket_append($out, $bucket);
}
return PSFS_PASS_ON;
}
}
class AES_128_CBC extends AES_CBC {
protected static function key_size() { return self::$KEY_SIZES['AES-128']; }
}
class AES_192_CBC extends AES_CBC {
protected static function key_size() { return self::$KEY_SIZES['AES-192']; }
}
class AES_256_CBC extends AES_CBC {
protected static function key_size() { return self::$KEY_SIZES['AES-256']; }
}
+add a note
User Contributed Notes 2 notes
Uwe Ohse ¶
7 years ago
1) The mcrypt module has been deprecated with PHP 7.1.
What does that mean for the encryption filters?
2) deriving the IV from the secret passphrase seems wrong. This leaks information about the passphrase without need.
3) using md5() for this? Really? hash() has been available since PHP 5.1.2.
4) hashing the passphrase twice does not add any security.
5) using substr() on binary data (why, oh why, is true passed to md5()?) might lead to headaches if mbstring.func_overload is used.
Maarten Bodewes ¶
7 years ago
The examples on this page should be destroyed with utmost urgency.
Strangely most people will fall over the use of generating the IV and 3DES key using MD5, a weak hash function, e.g. the previous note and CryptoFails blog.
http://www.cryptofails.com/post/70059608390/php-documentation-woes
A password based key derivation function should be used (bcrypt, PBKDF2).
However the use of MD-5 for key derivation however isn't that bad and if the password is strong enough (it usually isn't) then the generated DES ABC key is strong enough even now.
Using an identical IV for each password means that this function directly leaks information about the encrypted file. If the start of two encrypted files is identical then this function will directly leak information. For instance, if the routine encrypts multiple images then the JPEG header would be easily distinguished.
All in all these examples use deprecated routines (mcrypt), deprecated cryptographic functions (MD5 / DES) and then incorrectly perform the actual encryption. Enough reason to destroy them in the first place.
↑ and ↓ to navigate •
Enter to select •
Esc to close
Press Enter without
selection to search using Google