web-dev-qa-db-fra.com

Comment crypter et décrypter une chaîne PHP?

Ce que je veux dire est:

_Original String + Salt or Key --> Encrypted String
Encrypted String + Salt or Key --> Decrypted (Original String)
_

Peut-être quelque chose comme:

_"hello world!" + "ABCD1234" --> Encrypt --> "2a2ffa8f13220befbe30819047e23b2c" (may be, for e.g)
"2a2ffa8f13220befbe30819047e23b2c" --> Decrypt with "ABCD1234" --> "hello world!"
_
  • En PHP, comment pouvez-vous faire cela?

Tentative d'utilisation de Crypt_Blowfish, mais cela n'a pas fonctionné pour moi.

205
夏期劇場

Mis à jour

Version PHP 7 prête. Il utilise la fonction openssl_encrypt de PHP bibliothèque OpenSSL .

class Openssl_EncryptDecrypt {
    function encrypt ($pure_string, $encryption_key) {
        $cipher     = 'AES-256-CBC';
        $options    = OPENSSL_RAW_DATA;
        $hash_algo  = 'sha256';
        $sha2len    = 32;
        $ivlen = openssl_cipher_iv_length($cipher);
        $iv = openssl_random_pseudo_bytes($ivlen);
        $ciphertext_raw = openssl_encrypt($pure_string, $cipher, $encryption_key, $options, $iv);
        $hmac = hash_hmac($hash_algo, $ciphertext_raw, $encryption_key, true);
        return $iv.$hmac.$ciphertext_raw;
    }
    function decrypt ($encrypted_string, $encryption_key) {
        $cipher     = 'AES-256-CBC';
        $options    = OPENSSL_RAW_DATA;
        $hash_algo  = 'sha256';
        $sha2len    = 32;
        $ivlen = openssl_cipher_iv_length($cipher);
        $iv = substr($encrypted_string, 0, $ivlen);
        $hmac = substr($encrypted_string, $ivlen, $sha2len);
        $ciphertext_raw = substr($encrypted_string, $ivlen+$sha2len);
        $original_plaintext = openssl_decrypt($ciphertext_raw, $cipher, $encryption_key, $options, $iv);
        $calcmac = hash_hmac($hash_algo, $ciphertext_raw, $encryption_key, true);
        if(function_exists('hash_equals')) {
            if (hash_equals($hmac, $calcmac)) return $original_plaintext;
        } else {
            if ($this->hash_equals_custom($hmac, $calcmac)) return $original_plaintext;
        }
    }
    /**
     * (Optional)
     * hash_equals() function polyfilling.
     * PHP 5.6+ timing attack safe comparison
     */
    function hash_equals_custom($knownString, $userString) {
        if (function_exists('mb_strlen')) {
            $kLen = mb_strlen($knownString, '8bit');
            $uLen = mb_strlen($userString, '8bit');
        } else {
            $kLen = strlen($knownString);
            $uLen = strlen($userString);
        }
        if ($kLen !== $uLen) {
            return false;
        }
        $result = 0;
        for ($i = 0; $i < $kLen; $i++) {
            $result |= (ord($knownString[$i]) ^ ord($userString[$i]));
        }
        return 0 === $result;
    }
}

define('ENCRYPTION_KEY', '__^%&Q@$&*!@#$%^&*^__');
$string = "This is the original string!";

$OpensslEncryption = new Openssl_EncryptDecrypt;
$encrypted = $OpensslEncryption->encrypt($string, ENCRYPTION_KEY);
$decrypted = $OpensslEncryption->decrypt($encrypted, ENCRYPTION_KEY);
0
夏期劇場

Avant de faire autre chose, cherchez à comprendre la différence entre le cryptage et l’authentification , et pourquoi vous voulez probablement un cryptage authentifié plutôt que juste un cryptage .

Pour implémenter le cryptage authentifié, vous voulez crypter puis MAC. L'ordre de cryptage et d'authentification est très important! L'une des réponses existantes à cette question a commis cette erreur; comme le font de nombreuses bibliothèques de cryptographie écrites en PHP.

Vous devriez éviter de mettre en œuvre votre propre cryptographie et utiliser plutôt une bibliothèque sécurisée écrite et révisée par des experts en cryptographie.

Mise à jour: PHP 7.2 fournit maintenant libsodium ! Pour une sécurité optimale, mettez à jour votre système pour qu'il utilise PHP 7.2 ou version ultérieure, et ne suivez que les conseils de libsodium dans cette réponse.

tilisez libsodium si vous avez un accès PECL (ou sodium_compat si vous voulez libsodium sans PECL); sinon ...
tilisez defuse/php-encryption ; ne lancez pas votre propre cryptographie!

Les deux bibliothèques mentionnées ci-dessus facilitent et simplifient l’implémentation du chiffrement authentifié dans vos propres bibliothèques.

Si vous souhaitez toujours écrire et déployer votre propre bibliothèque de cryptographie, contrairement aux idées reçues de tous les experts en cryptographie sur Internet, vous devez suivre ces étapes.

Chiffrement:

  1. Chiffrer en utilisant AES en mode CTR. Vous pouvez également utiliser GCM (ce qui élimine la nécessité d'un MAC séparé). De plus, ChaCha20 et Salsa20 (fournis par libsodium ) sont des chiffrements de flux et ne nécessitent pas de modes spéciaux.
  2. Sauf si vous avez choisi GCM ci-dessus, vous devez authentifier le texte chiffré avec HMAC-SHA-256 (ou, pour les chiffrements de flux, Poly1305 - la plupart des API libsodium le font pour vous). Le MAC doit couvrir l'IV ainsi que le texte chiffré!

Décryptage:

  1. À moins que Poly1305 ou GCM ne soit utilisé, recalculez le MAC du texte chiffré et comparez-le avec le MAC envoyé à l'aide de hash_equals() . Si cela échoue, abandonnez.
  2. Décrypter le message.

Autres considérations de conception:

  1. Ne rien compresser jamais. Le texte chiffré n'est pas compressible; La compression du texte en clair avant le cryptage peut entraîner des fuites d’informations (par exemple, CRIME et BREACH sur TLS).
  2. Assurez-vous d'utiliser mb_strlen() et mb_substr() en utilisant le mode de jeu de caractères '8bit' pour éviter les problèmes de mbstring.func_overload.
  3. Les IV devraient être générés en utilisant un CSPRNG ; Si vous utilisez mcrypt_create_iv(), N'UTILISEZ PAS MCRYPT_Rand!
  4. Sauf si vous utilisez une construction AEAD, TOUJOURS chiffrer puis MAC!
  5. bin2hex(), base64_encode(), etc. peuvent divulguer des informations sur vos clés de chiffrement via la synchronisation du cache. Évitez-les si possible.

Même si vous suivez les conseils donnés ici, beaucoup de choses peuvent mal se passer avec la cryptographie. Demandez toujours à un expert en cryptographie d’évaluer votre implémentation. Si vous n’avez pas la chance d’être des amis personnels avec un étudiant en cryptographie de votre université locale, vous pouvez toujours essayer. le Cryptography Stack Exchange forum pour obtenir des conseils.

Si vous avez besoin d'une analyse professionnelle de votre mise en œuvre, vous pouvez toujours engager un équipe de consultants en sécurité renommée pour examiner votre code de cryptographie PHP (divulgation: par mon employeur).

Important: Quand ne pas utiliser le cryptage

Ne pas crypter mots de passe. Vous voulez les hacher à la place, en utilisant l'un de ces algorithmes de hachage de mot de passe:

N'utilisez jamais une fonction de hachage à usage général (MD5, SHA256) pour le stockage du mot de passe.

Ne pas chiffrer les paramètres d'URL . Ce n'est pas le bon outil pour le travail.

Exemple de cryptage de chaîne PHP avec Libsodium

Si vous êtes sur PHP <7.2 ou si vous n'avez pas installé libsodium, vous pouvez utiliser sodium_compat pour obtenir le même résultat (bien que plus lent).

<?php
declare(strict_types=1);

/**
 * Encrypt a message
 * 
 * @param string $message - message to encrypt
 * @param string $key - encryption key
 * @return string
 * @throws RangeException
 */
function safeEncrypt(string $message, string $key): string
{
    if (mb_strlen($key, '8bit') !== SODIUM_CRYPTO_SECRETBOX_KEYBYTES) {
        throw new RangeException('Key is not the correct size (must be 32 bytes).');
    }
    $nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);

    $cipher = base64_encode(
        $nonce.
        sodium_crypto_secretbox(
            $message,
            $nonce,
            $key
        )
    );
    sodium_memzero($message);
    sodium_memzero($key);
    return $cipher;
}

/**
 * Decrypt a message
 * 
 * @param string $encrypted - message encrypted with safeEncrypt()
 * @param string $key - encryption key
 * @return string
 * @throws Exception
 */
function safeDecrypt(string $encrypted, string $key): string
{   
    $decoded = base64_decode($encrypted);
    $nonce = mb_substr($decoded, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, '8bit');
    $ciphertext = mb_substr($decoded, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, null, '8bit');

    $plain = sodium_crypto_secretbox_open(
        $ciphertext,
        $nonce,
        $key
    );
    if (!is_string($plain)) {
        throw new Exception('Invalid MAC');
    }
    sodium_memzero($ciphertext);
    sodium_memzero($key);
    return $plain;
}

Puis pour le tester:

<?php
// This refers to the previous code block.
require "safeCrypto.php"; 

// Do this once then store it somehow:
$key = random_bytes(SODIUM_CRYPTO_SECRETBOX_KEYBYTES);
$message = 'We are all living in a yellow submarine';

$ciphertext = safeEncrypt($message, $key);
$plaintext = safeDecrypt($ciphertext, $key);

var_dump($ciphertext);
var_dump($plaintext);

Halite - Libsodium Made Easier

L'un des projets sur lequel j'ai travaillé est une bibliothèque de chiffrement appelée Halite , qui vise à rendre libsodium plus facile et plus intuitif.

<?php
use \ParagonIE\Halite\KeyFactory;
use \ParagonIE\Halite\Symmetric\Crypto as SymmetricCrypto;

// Generate a new random symmetric-key encryption key. You're going to want to store this:
$key = new KeyFactory::generateEncryptionKey();
// To save your encryption key:
KeyFactory::save($key, '/path/to/secret.key');
// To load it again:
$loadedkey = KeyFactory::loadEncryptionKey('/path/to/secret.key');

$message = 'We are all living in a yellow submarine';
$ciphertext = SymmetricCrypto::encrypt($message, $key);
$plaintext = SymmetricCrypto::decrypt($ciphertext, $key);

var_dump($ciphertext);
var_dump($plaintext);

Toute la cryptographie sous-jacente est gérée par libsodium.

Exemple avec defuse/php-encryption

<?php
/**
 * This requires https://github.com/defuse/php-encryption
 * php composer.phar require defuse/php-encryption
 */

use Defuse\Crypto\Crypto;
use Defuse\Crypto\Key;

require "vendor/autoload.php";

// Do this once then store it somehow:
$key = Key::createNewRandomKey();

$message = 'We are all living in a yellow submarine';

$ciphertext = Crypto::encrypt($message, $key);
$plaintext = Crypto::decrypt($ciphertext, $key);

var_dump($ciphertext);
var_dump($plaintext);

Remarque : Crypto::encrypt() renvoie une sortie codée en hexadécimal.

Gestion de la clé de cryptage

Si vous êtes tenté d'utiliser un "mot de passe", arrêtez-vous maintenant. Vous avez besoin d'une clé de chiffrement aléatoire de 128 bits et non d'un mot de passe mémorable.

Vous pouvez stocker une clé de chiffrement pour une utilisation à long terme, comme suit:

$storeMe = bin2hex($key);

Et, sur demande, vous pouvez le récupérer comme suit:

$key = hex2bin($storeMe);

Je recommande vivement de simplement stocker une clé générée aléatoirement pour une utilisation à long terme au lieu de tout type de mot de passe comme clé (ou pour dériver la clé).

Si vous utilisez la bibliothèque de Defuse:

"Mais je veux vraiment utiliser un mot de passe."

C'est une mauvaise idée, mais bon, voici comment le faire en toute sécurité.

Commencez par générer une clé aléatoire et enregistrez-la dans une constante.

/**
 * Replace this with your own salt! 
 * Use bin2hex() then add \x before every 2 hex characters, like so:
 */
define('MY_PBKDF2_SALT', "\x2d\xb7\x68\x1a\x28\x15\xbe\x06\x33\xa0\x7e\x0e\x8f\x79\xd5\xdf");

Notez que vous ajoutez du travail supplémentaire et que vous pouvez simplement utiliser cette constante comme clé et vous épargner beaucoup de chagrin d'amour!

Utilisez ensuite PBKDF2 (comme cela) pour obtenir une clé de chiffrement appropriée à partir de votre mot de passe plutôt que de chiffrer directement avec votre mot de passe.

/**
 * Get an AES key from a static password and a secret salt
 * 
 * @param string $password Your weak password here
 * @param int $keysize Number of bytes in encryption key
 */
function getKeyFromPassword($password, $keysize = 16)
{
    return hash_pbkdf2(
        'sha256',
        $password,
        MY_PBKDF2_SALT,
        100000, // Number of iterations
        $keysize,
        true
    );
}

Ne vous contentez pas d'utiliser un mot de passe de 16 caractères. Votre clé de cryptage sera comiquement cassée.

375

Je suis en retard pour la fête, mais en cherchant la bonne façon de le faire, je suis tombé sur cette page. C’était l’un des meilleurs résultats de recherche sur Google. J’aimerais donc partager mon point de vue sur le problème qu’il considère comme étant à jour au moment de la rédaction de cet article (début 2017). À partir de PHP 7.1.0, les mcrypt_decrypt et mcrypt_encrypt seront obsolètes. Par conséquent, la construction de code d'épreuve future doit utiliser openssl_encrypt et openssl_decrypt

Vous pouvez faire quelque chose comme:

$string_to_encrypt="Test";
$password="password";
$encrypted_string=openssl_encrypt($string_to_encrypt,"AES-128-ECB",$password);
$decrypted_string=openssl_decrypt($encrypted_string,"AES-128-ECB",$password);

Important : Ceci utilise mode ECB , qui n'est pas sécurisé. Si vous souhaitez une solution simple sans suivre de cours intensif en ingénierie de la cryptographie, ne l'écrivez pas vous-même, utilisez simplement une bibliothèque.

Vous pouvez également utiliser d'autres méthodes de déchiquetage, en fonction de vos besoins en matière de sécurité. Pour connaître les méthodes de déchiquetage disponibles, veuillez consulter la fonction openssl_get_cipher_methods .

53
Emil Borconi

Ce qu'il ne faut pas faire

AVERTISSEMENT:
Cette réponse utilise ECB . La BCE n’est pas un mode de cryptage, ce n’est qu’un bloc de construction. Utiliser ECB comme indiqué dans cette réponse ne chiffre pas réellement la chaîne en toute sécurité. N'utilisez pas la BCE dans votre code. Voir réponse de Scott pour une bonne solution.

Je l'ai sur moi-même. En fait, j'ai trouvé une réponse sur Google et juste modifié quelque chose. Le résultat est cependant complètement incertain

<?php
define("ENCRYPTION_KEY", "!@#$%^&*");
$string = "This is the original data string!";

echo $encrypted = encrypt($string, ENCRYPTION_KEY);
echo "<br />";
echo $decrypted = decrypt($encrypted, ENCRYPTION_KEY);

/**
 * Returns an encrypted & utf8-encoded
 */
function encrypt($pure_string, $encryption_key) {
    $iv_size = mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_ECB);
    $iv = mcrypt_create_iv($iv_size, MCRYPT_Rand);
    $encrypted_string = mcrypt_encrypt(MCRYPT_BLOWFISH, $encryption_key, utf8_encode($pure_string), MCRYPT_MODE_ECB, $iv);
    return $encrypted_string;
}

/**
 * Returns decrypted original string
 */
function decrypt($encrypted_string, $encryption_key) {
    $iv_size = mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_ECB);
    $iv = mcrypt_create_iv($iv_size, MCRYPT_Rand);
    $decrypted_string = mcrypt_decrypt(MCRYPT_BLOWFISH, $encryption_key, $encrypted_string, MCRYPT_MODE_ECB, $iv);
    return $decrypted_string;
}
?>
43
夏期劇場

Pour Laravel framework

Si vous utilisez le framework Laravel, il est plus facile de chiffrer et de déchiffrer avec des fonctions internes.

$string = 'Some text to be encrypted';
$encrypted = \Illuminate\Support\Facades\Crypt::encrypt($string);
$decrypted_string = \Illuminate\Support\Facades\Crypt::decrypt($encrypted);

var_dump($string);
var_dump($encrypted);
var_dump($decrypted_string);

Remarque: veillez à définir une chaîne aléatoire de 16, 24 ou 32 caractères dans l'option key du fichier config/app.php. Sinon, les valeurs chiffrées ne seront pas sécurisées.

13
Somnath Muluk

Note historique: Ceci a été écrit à l'époque de PHP4. C'est ce que nous appelons maintenant le "code hérité".

J'ai laissé cette réponse à des fins historiques - mais certaines méthodes sont désormais obsolètes, la méthode de chiffrement DES n'est pas une pratique recommandée, etc.

Je n'ai pas mis à jour ce code pour deux raisons: 1) je ne travaille plus avec des méthodes de chiffrement à la main en PHP, et 2) ce code sert toujours le but pour lequel il était destiné: montrer le concept minimal et simpliste de la manière dont le chiffrement peut fonctionner. en PHP.

Si vous trouvez un type de source aussi simpliste, "cryptage PHP pour les nuls", qui permet aux gens de commencer avec 10 à 20 lignes de code ou moins, faites-le-moi savoir dans les commentaires.

Au-delà, profitez de cet épisode classique de la réponse au cryptage minimaliste PHP4.


Idéalement, vous avez - ou pouvez obtenir - un accès à la bibliothèque mcrypt PHP, car elle est certainement très populaire et très utile pour une variété de tâches. Voici un aperçu des différents types de cryptage et d'un exemple de code: Techniques de cryptage en PHP

//Listing 3: Encrypting Data Using the mcrypt_ecb Function 

<?php 
echo("<h3> Symmetric Encryption </h3>"); 
$key_value = "KEYVALUE"; 
$plain_text = "PLAINTEXT"; 
$encrypted_text = mcrypt_ecb(MCRYPT_DES, $key_value, $plain_text, MCRYPT_ENCRYPT); 
echo ("<p><b> Text after encryption : </b>"); 
echo ( $encrypted_text ); 
$decrypted_text = mcrypt_ecb(MCRYPT_DES, $key_value, $encrypted_text, MCRYPT_DECRYPT); 
echo ("<p><b> Text after decryption : </b>"); 
echo ( $decrypted_text ); 
?> 

Quelques avertissements:

1) N'utilisez jamais de chiffrement réversible ou "symétrique" lorsqu'un hachage à sens unique fera l'affaire.

2) Si les données sont vraiment sensibles, comme les numéros de carte de crédit ou de sécurité sociale, arrêtez; vous avez besoin de plus que tout ce qu'un bloc de code fournira, mais vous avez plutôt besoin d'une crypto-bibliothèque conçue à cet effet et d'un temps considérable pour rechercher les méthodes nécessaires. De plus, la crypto logicielle représente probablement moins de 10% de la sécurité des données sensibles. C'est comme si vous re-câbliez une centrale nucléaire - acceptez le fait que la tâche est dangereuse et difficile et qu'elle dépasse votre connaissance si tel est le cas. Les pénalités financières peuvent être énormes, il est donc préférable d’utiliser un service et de leur en assumer la responsabilité.

3) Tout type de chiffrement facile à mettre en œuvre, répertorié ici, peut protéger raisonnablement les informations d'importance modérée que vous souhaitez éviter aux regards indiscrets ou limiter l'exposition en cas de fuite accidentelle/intentionnelle. Mais vu que la clé est stockée en texte brut sur le serveur Web, s’ils peuvent obtenir les données, ils peuvent obtenir la clé de déchiffrement.

Quoi qu'il en soit, amusez-vous bien :)

6
BrianH

Si vous ne voulez pas utiliser la bibliothèque (que vous devriez), utilisez quelque chose comme ceci (PHP 7):

function sign($message, $key) {
    return hash_hmac('sha256', $message, $key) . $message;
}

function verify($bundle, $key) {
    return hash_equals(
      hash_hmac('sha256', mb_substr($bundle, 64, null, '8bit'), $key),
      mb_substr($bundle, 0, 64, '8bit')
    );
}

function getKey($password, $keysize = 16) {
    return hash_pbkdf2('sha256',$password,'some_token',100000,$keysize,true);
}

function encrypt($message, $password) {
    $iv = random_bytes(16);
    $key = getKey($password);
    $result = sign(openssl_encrypt($message,'aes-256-ctr',$key,OPENSSL_RAW_DATA,$iv), $key);
    return bin2hex($iv).bin2hex($result);
}

function decrypt($hash, $password) {
    $iv = hex2bin(substr($hash, 0, 32));
    $data = hex2bin(substr($hash, 32));
    $key = getKey($password);
    if (!verify($data, $key)) {
      return null;
    }
    return openssl_decrypt(mb_substr($data, 64, null, '8bit'),'aes-256-ctr',$key,OPENSSL_RAW_DATA,$iv);
}

$string_to_encrypt='John Smith';
$password='password';
$encrypted_string=encrypt($string_to_encrypt, $password);
$decrypted_string=decrypt($encrypted_string, $password);
4
Ascon

Le code ci-dessous fonctionne en php pour toutes les chaînes de caractères spéciaux

   // Encrypt text --

    $token = "9611222007552";

      $cipher_method = 'aes-128-ctr';
      $enc_key = openssl_digest(php_uname(), 'SHA256', TRUE);  
      $enc_iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($cipher_method));  
      $crypted_token = openssl_encrypt($token, $cipher_method, $enc_key, 0, $enc_iv) . "::" . bin2hex($enc_iv);
    echo    $crypted_token;
    //unset($token, $cipher_method, $enc_key, $enc_iv);

    // Decrypt text  -- 

    list($crypted_token, $enc_iv) = explode("::", $crypted_token);  
      $cipher_method = 'aes-128-ctr';
      $enc_key = openssl_digest(php_uname(), 'SHA256', TRUE);
      $token = openssl_decrypt($crypted_token, $cipher_method, $enc_key, 0, hex2bin($enc_iv));
    echo   $token;
    //unset($crypted_token, $cipher_method, $enc_key, $enc_iv);
0
Javed