kutombawewe.net

Crittografia bidirezionale più semplice con PHP

Qual è il modo più semplice di eseguire la crittografia bidirezionale nelle comuni PHP installazioni?

Devo essere in grado di crittografare i dati con una chiave stringa e utilizzare la stessa chiave per decodificare sull'altra estremità.

La sicurezza non è tanto importante quanto la portabilità del codice, quindi mi piacerebbe essere in grado di mantenere le cose il più semplici possibile. Attualmente, sto usando un'implementazione RC4, ma se riesco a trovare qualcosa supportato in modo nativo, immagino di poter salvare un sacco di codice non necessario.

199
user1206970

A cura:

Dovresti davvero usare openssl_encrypt () & openssl_decrypt ()

Come Scott dice, Mcrypt non è una buona idea in quanto non è stato aggiornato dal 2007.

C'è anche un RFC per rimuovere Mcrypt da PHP - https://wiki.php.net/rfc/mcrypt-viking-funeral

177
472084

Importante : a meno che tu non abbia un molto caso d'uso particolare, non crittografare le password , utilizzare invece un algoritmo di hashing della password. Quando qualcuno dice di crittografare le proprie password in un'applicazione lato server, non sono informate o stanno descrivendo un design di sistema pericoloso. La memorizzazione sicura delle password è un problema completamente separato dalla crittografia.

Essere informato. Progettare sistemi sicuri.

Crittografia dati portatile in PHP

Se stai usando PHP 5.4 o successivo e non vuoi scrivere da solo un modulo di crittografia, ti consiglio di usare una libreria esistente che fornisce la crittografia autenticata . La libreria che ho collegato si basa solo su ciò che PHP fornisce ed è sotto revisione periodica da una manciata di ricercatori della sicurezza. (Incluso me stesso)

Se i tuoi obiettivi di portabilità non impediscono di richiedere estensioni PECL, libsodium è altamente consigliato su qualsiasi cosa tu o io possiamo scrivere in PHP.

Aggiornamento (12/06/2016): Ora puoi usare sodium_compat e usare lo stesso cripto libsodio offerte senza installare estensioni PECL.

Se vuoi cimentarti nell'ingegneria della crittografia, continua a leggere.


Innanzitutto, dovresti dedicare del tempo per imparare i pericoli della crittografia non autenticata e il Principio di Doom crittografico .

  • I dati crittografati possono ancora essere manomessi da un utente malintenzionato.
  • L'autenticazione dei dati crittografati impedisce la manomissione.
  • L'autenticazione dei dati non crittografati non impedisce la manomissione.

Crittografia e decrittografia

La crittografia in PHP è in realtà semplice (useremo openssl_encrypt() e openssl_decrypt() dopo aver preso alcune decisioni su come crittografare le tue informazioni, consulta openssl_get_cipher_methods() per un elenco dei metodi supportati sul tuo sistema.La scelta migliore è AES in CTR modalità :

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

Attualmente non vi è alcun motivo per credere che la dimensione della chiave AES sia un problema significativo di cui preoccuparsi (più grande è probabilmente non migliore, a causa di una cattiva programmazione della chiave nella modalità a 256 bit).

Nota: Non stiamo usando mcrypt perché è abandonware e ha bug non corretti che potrebbero influire sulla sicurezza. Per questi motivi, incoraggio altri sviluppatori PHP a evitarlo.

Semplice wrapper di crittografia/decrittografia che utilizza 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;
    }
}

Esempio di utilizzo

$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);

Demo : https://3v4l.org/jl7qR


La semplice libreria crittografica sopra non è ancora sicura da usare. Dobbiamo autenticare i testi cifrati e verificarli prima di decifrare .

Nota : per impostazione predefinita, UnsafeCrypto::encrypt() restituirà una stringa binaria non elaborata. Chiamalo così se hai bisogno di memorizzarlo in un formato sicuro da binari (codificato in 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);

Demo : http://3v4l.org/f5K93

Simple Authentication Wrapper

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

Esempio di utilizzo

$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);

Demos : raw binary , base64-encoded


Se qualcuno desidera utilizzare questa libreria SaferCrypto in un ambiente di produzione o la propria implementazione degli stessi concetti, consiglio vivamente di contattare i crittografi residenti per una seconda opinione prima di te. Potranno parlarti di errori di cui forse non ero a conoscenza.

Sarà molto meglio usare una libreria di crittografia affidabile .

199

Usa mcrypt_encrypt() e mcrypt_decrypt() con i parametri corrispondenti. Davvero facile e diretto, e si utilizza un pacchetto di crittografia testato in battaglia.

MODIFICA

5 anni e 4 mesi dopo questa risposta, l'estensione mcrypt è ora in fase di deprecazione ed eventuale rimozione da PHP.

22
Eugen Rieck

PHP 7.2 si è spostato completamente da Mcrypt e la crittografia ora è basata sulla libreria Libsodium gestibile.

Tutte le tue esigenze di crittografia possono essere sostanzialmente risolte attraverso la libreria Libsodium.

// 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."
}

Documentazione di Libsodium: https://github.com/paragonie/pecl-libsodium-doc

3
Hemerson Varela

Ecco un'implementazione semplice ma sicura:

  • Crittografia AES-256 in modalità CBC
  • PBKDF2 per creare una chiave di crittografia con password di testo normale
  • HMAC per autenticare il messaggio crittografato.

Codice ed esempi sono qui: https://stackoverflow.com/a/19445173/1387163

2
Eugene Fidelin