285 votes

Le cryptage bidirectionnel le plus simple en PHP

Quel est le moyen le plus simple de réaliser un cryptage à double sens dans les installations PHP courantes ?

Je dois pouvoir crypter des données avec une clé de type chaîne et utiliser la même clé pour décrypter à l'autre bout.

La sécurité n'est pas une préoccupation aussi importante que la portabilité du code, et j'aimerais donc pouvoir garder les choses aussi simples que possible. Actuellement, j'utilise une implémentation RC4, mais si je peux trouver quelque chose de supporté nativement, je pense que je peux économiser beaucoup de code inutile.

7 votes

3 votes

Pour un chiffrement à usage général, utilisez defuse/php-encryption/ au lieu d'en faire un.

3 votes

Mains éloignées de github.com/defuse/php-encryption - il est plus lent de plusieurs ordres de grandeur que Mcrypt.

292voto

Scott Points 389

Important : A moins que vous n'ayez un très un cas d'utilisation particulier, ne pas crypter les mots de passe utilisez plutôt un algorithme de hachage de mot de passe. Lorsque quelqu'un dit qu'il crypter leurs mots de passe dans une application côté serveur, soit ils sont mal informés, soit ils décrivent une conception de système dangereuse. Stocker les mots de passe en toute sécurité est un problème totalement distinct du cryptage.

Soyez informé. Concevez des systèmes sûrs.

Cryptage de données portable en PHP

Si vous utilisez PHP 5.4 ou plus récent et que vous ne voulez pas écrire vous-même un module de cryptographie, je vous recommande d'utiliser une bibliothèque existante qui fournit un cryptage authentifié . La bibliothèque que j'ai liée ne s'appuie que sur ce que PHP fournit et fait l'objet d'une révision périodique par une poignée de chercheurs en sécurité. (Moi-même inclus.)

Si vos objectifs de portabilité n'empêchent pas de demander des extensions PECL, libsodium es hautement recommandé par rapport à tout ce que vous ou moi pouvons écrire en PHP.

Mise à jour (2016-06-12) : Vous pouvez maintenant utiliser sodium_compat et utiliser la même crypto libsodium offre sans installer les extensions PECL.

Si vous voulez vous essayer à l'ingénierie de la cryptographie, lisez la suite.


Tout d'abord, vous devez prendre le temps d'apprendre les dangers du cryptage non authentifié y le principe du Doom cryptographique .

  • Les données cryptées peuvent toujours être altérées par un utilisateur malveillant.
  • L'authentification des données cryptées empêche la falsification.
  • L'authentification des données non cryptées n'empêche pas la falsification.

Cryptage et décryptage

Le cryptage en PHP est en fait simple (nous allons utiliser la fonction openssl_encrypt() y openssl_decrypt() une fois que vous aurez pris des décisions sur la manière de crypter vos informations. Consultez openssl_get_cipher_methods() pour obtenir la liste des méthodes prises en charge par votre système. Le meilleur choix est AES en mode CTR :

  • aes-128-ctr
  • aes-192-ctr
  • aes-256-ctr

Il n'y a actuellement aucune raison de croire que le Taille de la clé AES est un problème important dont il faut s'inquiéter (plus important est probablement pas mieux, en raison d'un mauvais ordonnancement des clés dans le mode 256 bits).

Nota: Nous n'utilisons pas mcrypt parce que c'est abandonware et a bogues non corrigés qui pourraient affecter la sécurité. Pour ces raisons, j'encourage les autres développeurs PHP à l'éviter également.

Wrapper de chiffrement/déchiffrement simple utilisant OpenSSL

class UnsafeCrypto
{
    const METHOD = 'aes-256-ctr';

    /**
     * Encrypts (but does not authenticate) a message
     * 
     * @param string $message - plaintext message
     * @param string $key - encryption key (raw binary expected)
     * @param boolean $encode - set to TRUE to return a base64-encoded 
     * @return string (raw binary)
     */
    public static function encrypt($message, $key, $encode = false)
    {
        $nonceSize = openssl_cipher_iv_length(self::METHOD);
        $nonce = openssl_random_pseudo_bytes($nonceSize);

        $ciphertext = openssl_encrypt(
            $message,
            self::METHOD,
            $key,
            OPENSSL_RAW_DATA,
            $nonce
        );

        // Now let's pack the IV and the ciphertext together
        // Naively, we can just concatenate
        if ($encode) {
            return base64_encode($nonce.$ciphertext);
        }
        return $nonce.$ciphertext;
    }

    /**
     * Decrypts (but does not verify) a message
     * 
     * @param string $message - ciphertext message
     * @param string $key - encryption key (raw binary expected)
     * @param boolean $encoded - are we expecting an encoded string?
     * @return string
     */
    public static function decrypt($message, $key, $encoded = false)
    {
        if ($encoded) {
            $message = base64_decode($message, true);
            if ($message === false) {
                throw new Exception('Encryption failure');
            }
        }

        $nonceSize = openssl_cipher_iv_length(self::METHOD);
        $nonce = mb_substr($message, 0, $nonceSize, '8bit');
        $ciphertext = mb_substr($message, $nonceSize, null, '8bit');

        $plaintext = openssl_decrypt(
            $ciphertext,
            self::METHOD,
            $key,
            OPENSSL_RAW_DATA,
            $nonce
        );

        return $plaintext;
    }
}

Exemple d'utilisation

$message = 'Ready your ammunition; we attack at dawn.';
$key = hex2bin('000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f');

$encrypted = UnsafeCrypto::encrypt($message, $key);
$decrypted = UnsafeCrypto::decrypt($encrypted, $key);

var_dump($encrypted, $decrypted);

Démo : https://3v4l.org/jl7qR


La bibliothèque cryptographique simple ci-dessus n'est toujours pas sûre à utiliser. Nous devons authentifier les cryptogrammes et les vérifier avant de les décrypter. .

Note : Par défaut, UnsafeCrypto::encrypt() retournera une chaîne binaire brute. Appelez-la de cette façon si vous avez besoin de la stocker dans un format sûr pour les binaires (encodé en base64) :

$message = 'Ready your ammunition; we attack at dawn.';
$key = hex2bin('000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f');

$encrypted = UnsafeCrypto::encrypt($message, $key, true);
$decrypted = UnsafeCrypto::decrypt($encrypted, $key, true);

var_dump($encrypted, $decrypted);

Démo : http://3v4l.org/f5K93

Wrapper d'authentification simple

class SaferCrypto extends UnsafeCrypto
{
    const HASH_ALGO = 'sha256';

    /**
     * Encrypts then MACs a message
     * 
     * @param string $message - plaintext message
     * @param string $key - encryption key (raw binary expected)
     * @param boolean $encode - set to TRUE to return a base64-encoded string
     * @return string (raw binary)
     */
    public static function encrypt($message, $key, $encode = false)
    {
        list($encKey, $authKey) = self::splitKeys($key);

        // Pass to UnsafeCrypto::encrypt
        $ciphertext = parent::encrypt($message, $encKey);

        // Calculate a MAC of the IV and ciphertext
        $mac = hash_hmac(self::HASH_ALGO, $ciphertext, $authKey, true);

        if ($encode) {
            return base64_encode($mac.$ciphertext);
        }
        // Prepend MAC to the ciphertext and return to caller
        return $mac.$ciphertext;
    }

    /**
     * Decrypts a message (after verifying integrity)
     * 
     * @param string $message - ciphertext message
     * @param string $key - encryption key (raw binary expected)
     * @param boolean $encoded - are we expecting an encoded string?
     * @return string (raw binary)
     */
    public static function decrypt($message, $key, $encoded = false)
    {
        list($encKey, $authKey) = self::splitKeys($key);
        if ($encoded) {
            $message = base64_decode($message, true);
            if ($message === false) {
                throw new Exception('Encryption failure');
            }
        }

        // Hash Size -- in case HASH_ALGO is changed
        $hs = mb_strlen(hash(self::HASH_ALGO, '', true), '8bit');
        $mac = mb_substr($message, 0, $hs, '8bit');

        $ciphertext = mb_substr($message, $hs, null, '8bit');

        $calculated = hash_hmac(
            self::HASH_ALGO,
            $ciphertext,
            $authKey,
            true
        );

        if (!self::hashEquals($mac, $calculated)) {
            throw new Exception('Encryption failure');
        }

        // Pass to UnsafeCrypto::decrypt
        $plaintext = parent::decrypt($ciphertext, $encKey);

        return $plaintext;
    }

    /**
     * Splits a key into two separate keys; one for encryption
     * and the other for authenticaiton
     * 
     * @param string $masterKey (raw binary)
     * @return array (two raw binary strings)
     */
    protected static function splitKeys($masterKey)
    {
        // You really want to implement HKDF here instead!
        return [
            hash_hmac(self::HASH_ALGO, 'ENCRYPTION', $masterKey, true),
            hash_hmac(self::HASH_ALGO, 'AUTHENTICATION', $masterKey, true)
        ];
    }

    /**
     * Compare two strings without leaking timing information
     * 
     * @param string $a
     * @param string $b
     * @ref https://paragonie.com/b/WS1DLx6BnpsdaVQW
     * @return boolean
     */
    protected static function hashEquals($a, $b)
    {
        if (function_exists('hash_equals')) {
            return hash_equals($a, $b);
        }
        $nonce = openssl_random_pseudo_bytes(32);
        return hash_hmac(self::HASH_ALGO, $a, $nonce) === hash_hmac(self::HASH_ALGO, $b, $nonce);
    }
}

Exemple d'utilisation

$message = 'Ready your ammunition; we attack at dawn.';
$key = hex2bin('000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f');

$encrypted = SaferCrypto::encrypt($message, $key);
$decrypted = SaferCrypto::decrypt($encrypted, $key);

var_dump($encrypted, $decrypted);

Démos : binaire brut , base64-encodé


Si quelqu'un souhaite utiliser cette SaferCrypto dans un environnement de production, ou votre propre mise en œuvre des mêmes concepts, je vous recommande vivement de vous adresser à vos cryptographes résidents pour un second avis avant de le faire. Ils pourront vous parler d'erreurs dont je ne suis peut-être même pas au courant.

Vous ferez mieux d'utiliser une bibliothèque de cryptographie réputée .

208voto

472084 Points 9104

Édité :

Vous devriez vraiment utiliser openssl_encrypt() & openssl_decrypt()

Comme Scott dit, Mcrypt n'est pas une bonne idée car il n'a pas été mis à jour depuis 2007.

Il existe même une RFC pour supprimer Mcrypt de PHP -. https://wiki.php.net/rfc/mcrypt-viking-funeral

6 votes

@EugenRieck Oui, c'est le but. Mcrypt ne reçoit pas de correctifs. OpenSSL reçoit des correctifs dès qu'une vulnérabilité, petite ou grande, est découverte.

7 votes

Il serait mieux pour une réponse ayant reçu un grand nombre de votes, de fournir des exemples simples dans la réponse. merci quand même.

0 votes

Les gars, juste pour info => MCRYPT EST DÉPRÉCIÉ. capsing donc tout le monde devrait savoir de ne pas l'utiliser car il nous a donné une myriade de problèmes. Il est déprécié depuis PHP 7.1 si je ne me trompe pas.

22voto

Eugen Rieck Points 33670

Utilisez mcrypt_encrypt() y mcrypt_decrypt() avec les paramètres correspondants. C'est très simple et direct, et vous utilisez un logiciel de cryptage éprouvé.

EDITAR

5 ans et 4 mois après cette réponse, la mcrypt est maintenant en cours de dépréciation et sera éventuellement supprimée de PHP.

39 votes

Testé en bataille et non mis à jour depuis plus de 8 ans ?

5 votes

Eh bien, mcrypt est en PHP7 et n'est pas déprécié - c'est suffisant pour moi. Tout le code n'est pas de l'horrible qualité d'OpenSSL et a besoin de Parcheando tous les quelques jours.

4 votes

Mcrypt n'est pas seulement horrible en ce qui concerne le support. Il n'implémente pas non plus les meilleures pratiques comme le rembourrage conforme à la norme PKCS#7, le cryptage authentifié. Il ne supportera pas SHA-3 ou tout autre nouvel algorithme car personne ne le maintient, vous privant ainsi d'un chemin de mise à jour. De plus, il acceptait des choses comme les clés partielles, le remplissage à zéro, etc. Il y a une bonne raison pour laquelle il est en train d'être progressivement retiré de PHP.

13voto

harshal lonare Points 49

Cryptage avec openssl_encrypt() La fonction openssl_encrypt fournit un moyen simple et sécurisé de crypter vos données.

Dans le script ci-dessous, nous utilisons la méthode de chiffrement AES128, mais vous pouvez envisager un autre type de méthode de chiffrement en fonction de ce que vous voulez chiffrer.

<?php
$message_to_encrypt = "Yoroshikune";
$secret_key = "my-secret-key";
$method = "aes128";
$iv_length = openssl_cipher_iv_length($method);
$iv = openssl_random_pseudo_bytes($iv_length);

$encrypted_message = openssl_encrypt($message_to_encrypt, $method, $secret_key, 0, $iv);

echo $encrypted_message;
?>

Voici une explication des variables utilisées :

message_to_encrypt : les données que vous voulez crypter secret_key : c'est votre 'mot de passe' pour le cryptage. Veillez à ne pas choisir quelque chose de trop facile et faites attention à ne pas partager votre clé secrète avec d'autres personnes. method : la méthode de cryptage. Ici, nous avons choisi AES128. iv_length et iv : préparez le cryptage en utilisant des octets. encrypted_message : la variable contenant votre message crypté.

Décryptage avec openssl_decrypt() Maintenant que vous avez crypté vos données, vous pouvez avoir besoin de les décrypter afin de réutiliser le message que vous avez d'abord inclus dans une variable. Pour ce faire, nous allons utiliser la fonction openssl_decrypt().

<?php
$message_to_encrypt = "Yoroshikune";
$secret_key = "my-secret-key";
$method = "aes128";
$iv_length = openssl_cipher_iv_length($method);
$iv = openssl_random_pseudo_bytes($iv_length);
$encrypted_message = openssl_encrypt($message_to_encrypt, $method, $secret_key, 0, $iv);

$decrypted_message = openssl_decrypt($encrypted_message, $method, $secret_key, 0, $iv);

echo $decrypted_message;
?>

La méthode de décryptage proposée par openssl_decrypt() est proche de openssl_encrypt().

La seule différence est qu'au lieu d'ajouter $message_to_encrypt, vous devrez ajouter votre message déjà crypté comme premier argument de openssl_decrypt().

Note : La clé secrète et l'iv doivent être sauvegardées afin de pouvoir décrypter.

6voto

Hemerson Varela Points 1890

PHP 7.2 s'est éloigné complètement de Mcrypt et le cryptage est maintenant basé sur le maintenable Libsodium bibliothèque.

Tous vos besoins en matière de cryptage peuvent être résolus de manière basique grâce à Libsodium bibliothèque.

// On Alice's computer:
$msg = 'This comes from Alice.';
$signed_msg = sodium_crypto_sign($msg, $secret_sign_key);

// On Bob's computer:
$original_msg = sodium_crypto_sign_open($signed_msg, $alice_sign_publickey);
if ($original_msg === false) {
    throw new Exception('Invalid signature');
} else {
    echo $original_msg; // Displays "This comes from Alice."
}

Documentation de Libsodium : https://github.com/paragonie/pecl-libsodium-doc

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