Filtres de chiffrement

Avertissement

Cette fonctionalité est devenue OBSOLETE depuis PHP 7.1.0. Nous vous encourageons vivement à ne plus l'utiliser.

Les filtres de chiffrement sont particulièrement utiles pour le chiffrement des fichiers/flux.

mcrypt.* et mdecrypt.* fournissent un système de chiffrement symétrique basé sur libmcrypt. Les deux filtres supportent les mêmes algorithmes disponibles avec la bibliothèque mcrypt, sous la forme de la directive mcrypt.ciphernameciphername est le nom du chiffrement qui sera passé à mcrypt_module_open(). Les cinq paramètres suivants sont aussi disponibles :

Paramètres du filtre mcrypt
Paramètre Obligatoire Par défaut Valeurs possibles
mode Optionnel cbc cbc, cfb, ecb, nofb, ofb, stream
algorithms_dir Optionnel ini_get('mcrypt.algorithms_dir') Chemin vers les algorithmes de modules
modes_dir Optionnel ini_get('mcrypt.modes_dir') Chemin vers les modes de modules
iv Obligatoire N/A Généralement 8, 16 ou 32 octets de données binaires. Dépend du chiffrement
key Obligatoire N/A Généralement 8, 16 ou 32 octets de données binaires. Dépend du chiffrement

Exemple #1 Chiffrement/déchiffrement avec Blowfish

<?php
//$key supposé être précédemment généré
$iv_size mcrypt_get_iv_size(MCRYPT_BLOWFISHMCRYPT_MODE_CBC);
$iv mcrypt_create_iv($iv_sizeMCRYPT_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);

//déchiffrement...
$fp fopen('encrypted-file.enc''rb');
$iv fread($fp$iv_size mcrypt_get_iv_size(MCRYPT_BLOWFISHMCRYPT_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;
?>

Exemple #2 Chiffrer un fichier en utilisant AES-128 CBC avec SHA256 HMAC en PHP 5.5+

<?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']; } //par défaut AES-128
    
public static function encryptFile($password$input_stream$aes_filename){
        
$iv_size mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128MCRYPT_MODE_CBC);
        
$fin fopen($input_stream"rb");
        
$fc fopen($aes_filename"wb+");
        if (!empty(
$fin) && !empty($fc)) {
            
fwrite($fcstr_repeat("_"32) );//placeholder, SHA256 HMAC will go here later
            
fwrite($fc$hmac_salt mcrypt_create_iv($iv_sizeMCRYPT_DEV_URANDOM));
            
fwrite($fc$esalt mcrypt_create_iv($iv_sizeMCRYPT_DEV_URANDOM));
            
fwrite($fc$iv mcrypt_create_iv($iv_sizeMCRYPT_DEV_URANDOM));
            
$ekey hash_pbkdf2("sha256"$password$esalt$it=1000self::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($fin8192);
                
$infilesize+=strlen($block);
                
fwrite($fc$block);
            }
            
$block_size mcrypt_get_block_size(MCRYPT_RIJNDAEL_128MCRYPT_MODE_CBC);
            
$padding $block_size - ($infilesize $block_size);//$padding is a number from 1-16
            
fwrite($fcstr_repeat(chr($padding), $padding) );//réalise un rembourrage PKCS7
            
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_128MCRYPT_MODE_CBC);
        
$hmac_raw file_get_contents($aes_filenamefalseNULL,  032);
        
$hmac_salt file_get_contents($aes_filenamefalseNULL32$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($fc32+$iv_size);//skip sha256 hmac and salt
            
$esalt fread($fc$iv_size);
            
$iv    fread($fc$iv_size);
            
$ekey hash_pbkdf2("sha256"$password$esalt$it=1000self::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($fc8192);
                if (
feof($fc)) {
                    
$padding ord($block[strlen($block) - 1]);//présume un rembourrage PKCS7
                    
$block substr($block00-$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=100024$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 add a note

User Contributed Notes 2 notes

up
7
Uwe Ohse
9 months 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.
up
3
Maarten Bodewes
4 months 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.
To Top