270 votes

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

Ce que je veux dire, c'est que :

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 faire ?

_(Tentative d'utilisation de Crypt_Blowfish mais cela n'a pas fonctionné pour moi)_

43 votes

@Rogue Il ne veut pas de hachage, il veut un cryptage symétrique (comme AES), mais il ne sait pas comment ça s'appelle. (Et maintenant il le sait :) )

0 votes

Quelle doit être la sécurité ?

3 votes

@, Vous ne faites pas de cryptage symétrique "salé", vous utilisez une clé. Une clé doit être gardée secrète. Un sel peut être public sans nuire à la sécurité (tant que le sel de chacun est différent), et c'est un terme utilisé dans le hachage des mots de passe.

471voto

Scott Points 389

Avant de faire quoi que ce soit d'autre, cherchez à comprendre la différence entre cryptage et authentification et pourquoi vous voulez probablement cryptage authentifié plutôt que juste cryptage .

Pour mettre en œuvre le cryptage authentifié, vous voulez Crypter puis MAC. L'ordre du cryptage et de l'authentification est très important ! L'une des réponses existantes à cette question fait cette erreur, tout comme de nombreuses bibliothèques de cryptographie écrites en PHP.

Vous devez éviter d'implémenter 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 meilleure sécurité, mettez à jour vos systèmes pour utiliser PHP 7.2 ou plus et suivez uniquement les conseils de libsodium dans cette réponse.

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

Les deux bibliothèques liées ci-dessus permettent de mettre en œuvre facilement et sans difficulté le cryptage authentifié dans vos propres bibliothèques.

Si vous souhaitez toujours écrire et déployer votre propre bibliothèque de cryptographie, contre la sagesse conventionnelle de tous les experts en cryptographie sur Internet, voici les étapes que vous devrez suivre.

Le cryptage :

  1. Cryptez en utilisant AES en mode CTR. Vous pouvez également utiliser le GCM (qui supprime la nécessité d'un MAC séparé). En outre, ChaCha20 et Salsa20 (fournis par le ministère des Affaires étrangères et du Commerce international) peuvent être utilisés. libsodium ) sont des ciphers à flux et ne nécessitent pas de modes spéciaux.
  2. À moins que vous n'ayez choisi GCM ci-dessus, vous devriez authentifier le texte chiffré avec HMAC-SHA-256 (ou, pour les chiffrages de flux, Poly1305 -- la plupart des API libsodium le font pour vous). Le MAC doit couvrir le 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 au MAC qui a été envoyé à l'aide de hash_equals() . S'il échoue, abandonnez.
  2. Décryptez le message.

Autres considérations de conception :

  1. Ne comprimez jamais rien. Le texte chiffré n'est pas compressible ; la compression du texte en clair avant le chiffrement peut entraîner des fuites d'informations (par exemple, CRIME et BREACH sur TLS).
  2. Veillez à utiliser mb_strlen() et mb_substr() en utilisant le '8bit' mode de jeu de caractères pour empêcher mbstring.func_overload problèmes.
  3. Les IV doivent être générés en utilisant un CSPRNG Si vous utilisez mcrypt_create_iv() , NE PAS UTILISER MCRYPT_RAND !
  4. A moins que vous n'utilisiez une construction AEAD, TOUJOURS crypter 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 tourner avec la cryptographie. Demandez toujours à un expert en cryptographie de revoir votre mise en œuvre. Si vous n'avez pas la chance d'être personnellement ami avec un étudiant en cryptographie de votre université locale, vous pouvez toujours essayer le site Web de la Commission européenne. Cryptographie Stack Exchange pour obtenir des conseils.

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

Important : Quand ne pas utiliser le chiffrement

Ne fais pas ça. crypter mots de passe . Vous voulez dièse à la place, en utilisant l'un de ces algorithmes de cryptage de mot de passe :

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

Ne pas crypter les paramètres d'URL . C'est le mauvais outil pour le travail.

Exemple de chiffrement de chaînes en PHP avec Libsodium

Si vous utilisez 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 lentement).

<?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 de 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 - Le libsodium en toute simplicité

Un des projets sur lequel j'ai travaillé est une bibliothèque de cryptage 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);

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

Gestion des clés de cryptage

Si vous êtes tenté d'utiliser un "mot de passe", arrêtez tout de suite. Vous avez besoin d'une clé de cryptage 128 bits aléatoire, pas d'un mot de passe mémorisable par l'homme.

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

$storeMe = bin2hex($key);

Et, à la demande, vous pouvez le récupérer comme ceci :

$key = hex2bin($storeMe);

I fortement recommande de simplement stocker une clé générée aléatoirement pour une utilisation à long terme au lieu d'utiliser un mot de passe comme clé (ou pour dériver la clé).

Si vous utilisez la bibliothèque de Defuse :

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

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

Tout d'abord, générez une clé aléatoire et stockez-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 pourriez simplement utiliser cette constante comme clé et vous épargner bien des soucis !

Utilisez ensuite PBKDF2 (comme ceci) pour dériver 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
    );
}

N'utilisez pas seulement un mot de passe à 16 caractères. Votre clé de cryptage sera ridiculement cassée.

8 votes

Ne chiffrez pas les mots de passe et les hacher avec password_hash() et les vérifier avec password_verify() .

3 votes

"Ne jamais compresser quoi que ce soit." Vous voulez dire comme le font HTTP, spdy et d'autres protocoles ? Avant TLS ? Un conseil absolutiste ?

1 votes

@ScottArciszewski J'aime votre commentaire à propos de la clé "// Faire cela une fois puis le stocker d'une manière ou d'une autre :" d'une manière ou d'une autre, lol :)) eh bien, que diriez-vous de stocker cette 'clé' (qui est un objet) comme une chaîne de caractères simple, codée en dur ? J'ai besoin de la clé elle-même, sous forme de chaîne. Puis-je l'obtenir de cet objet d'une manière ou d'une autre ? Merci

123voto

Emil Borconi Points 1031

Je suis en retard sur la fête, mais en cherchant la façon correcte de le faire, je suis tombé sur cette page c'était l'un des premiers retours de recherche Google, donc je voudrais partager mon point de vue sur le problème, que je considère comme étant à jour au moment de la rédaction de ce post (début 2017). À partir de PHP 7.1.0, le mcrypt_decrypt et mcrypt_encrypt va être déprécié, donc pour construire du code à l'épreuve du futur, il faut 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 : Cela utilise Mode BCE ce qui n'est pas sûr. Si vous voulez une solution simple sans prendre un cours intensif d'ingénierie en 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 votre besoin de sécurité. Pour connaître les méthodes de déchiquetage disponibles, veuillez consulter la page openssl_get_cipher_methods fonction.

25 votes

Merci, je suis surpris que cette réponse simple et claire ne fasse pas l'objet de plus de votes. Je préfère ne pas lire une discussion de 10 pages sur le sujet comme la réponse du haut alors que tout ce que je veux c'est crypter/décrypter une simple chaîne de caractères.

6 votes

Ce n'est pas une réponse sûre. Mode BCE ne devrait pas être utilisé. Si vous voulez une "réponse simple et claire", il suffit de utiliser une bibliothèque .

2 votes

@ScottArciszewski, oui j'admets avoir parlé trop vite en cherchant un code simple. J'ai depuis ajouté un IV et utilisé le CBC dans mon propre code, ce qui est suffisant pour mon utilisation.

45voto

夏期劇場 Points 2206

Je l'ai fait moi-même. En fait, j'ai trouvé une réponse sur Google et j'ai juste modifié quelque chose. Quoi qu'il en soit, voici celui que je voulais et il est juste assez bon. Pas besoin de plus de complication.

<?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;
}
?>

Merci à tous !

8 votes

Vous pouvez utiliser "MCRYPT_MODE_CBC" au lieu de "MCRYPT_MODE_ECB" pour garantir une plus grande sécurité.

1 votes

MCRYPT_MODE_ECB n'utilise pas l'IV

1 votes

Voici l'implémentation du cryptage AES-128 en mode CBC stackoverflow.com/a/19445173/1387163

25voto

Somnath Muluk Points 10173

Pour le cadre Laravel

Si vous utilisez le framework Laravel, il est plus facile de crypter et décrypter avec les 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 de l'option key du fichier config/app.php. Sinon, les valeurs cryptées ne seront pas sécurisées.

2 votes

Bien sûr, il peut être facile à utiliser. Mais est-il sûr ? Comment répond-il aux problèmes de stackoverflow.com/a/30159120/781723 ? Utilise-t-il un chiffrement authentifié ? Évite-t-il les vulnérabilités des canaux latéraux et assure-t-il des contrôles d'égalité en temps constant ? Utilise-t-il une clé véritablement aléatoire plutôt qu'un mot de passe ou une phrase de passe ? Utilise-t-il un mode de fonctionnement approprié ? Génère-t-il correctement des IV aléatoires ?

21voto

夏期劇場 Points 2206

Mise à jour de

Version prête pour PHP 7. Elle utilise openssl_encrypt de la fonction 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);

1 votes

Cette version est meilleure et beaucoup plus sûre. Merci, elle fonctionne aussi comme prévu.

3 votes

Comment créez-vous et où conservez-vous la clé de cryptage ?

0 votes

Mais, le texte crypté a changé chaque fois, comment puis-je comparer la chaîne cryptée stockée avec elle ?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X