web-dev-qa-db-esp.com

¿Cómo se encripta y desencripta un PHP ¿Cuerda?

Lo que quiero decir es:

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

Quizás algo como:

"hello world!" + "ABCD1234" --> Encrypt --> "2a2ffa8f13220befbe30819047e23b2c" (may be, for e.g)
"2a2ffa8f13220befbe30819047e23b2c" --> Decrypt with "ABCD1234" --> "hello world!"
  • En PHP, ¿cómo puedes hacer esto?

Intenté usarCrypt_Blowfish, pero no funcionó para mí.

192
夏期劇場

Qué no hacer

ADVERTENCIA:
Esta respuesta usa ECB . El BCE no es un modo de encriptación, es solo un bloque de construcción. El uso del BCE como se demuestra en esta respuesta no encripta la cadena de forma segura. No utilice ECB en su código. Ver la respuesta de Scott para una buena solución.

Lo tengo en mí mismo. En realidad encontré una respuesta en google y solo modifiqué algo. El resultado es completamente inseguro sin embargo.

<?php
define("ENCRYPTION_KEY", "[email protected]#$%^&*");
$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;
}
?>
44
夏期劇場

Antes de seguir adelante, intente comprender la diferencia entre cifradoy autenticación , y por qué es probable que desee cifrado autenticado en lugar de solo cifrado.

Para implementar el cifrado autenticado, desea cifrar y luego MAC. El orden de encriptación y autenticación es muy importante! Una de las respuestas existentes a esta pregunta cometió este error; al igual que muchas bibliotecas de criptografía escritas en PHP.

Debe evitar la implementación de su propia criptografía y, en su lugar, utilizar una biblioteca segura escrita y revisada por expertos en criptografía.

Actualización: PHP 7.2 ahora proporciona libsodium! Para mayor seguridad, actualice sus sistemas para usar PHP 7.2 o superior y solo siga las recomendaciones de libsodium en esta respuesta.

Use libsodium si tiene acceso a PECL (o sodium_compat si desea libsodium sin PECL); de lo contrario ...
Use defuse/php-encryption ; ¡no haga su propia criptografía!

Las dos bibliotecas vinculadas anteriormente hacen que sea fácil y sencillo implementar el cifrado autenticado en sus propias bibliotecas.

Si aún desea escribir e implementar su propia biblioteca de criptografía, en contra de la sabiduría convencional de todos los expertos en criptografía en Internet, estos son los pasos que debe seguir.

Cifrado

  1. Cifrar utilizando AES en modo CTR. También puede usar GCM (que elimina la necesidad de un MAC por separado). Además, ChaCha20 y Salsa20 (proporcionados por libsodium ) son cifrados de flujo y no necesitan modos especiales.
  2. A menos que elija GCM arriba, debe autenticar el texto cifrado con HMAC-SHA-256 (o, para los cifrados de flujo, Poly1305: la mayoría de las API de libsodium lo hacen por usted). ¡El MAC debe cubrir la IV así como el texto cifrado!

Descifrado:

  1. A menos que se utilice Poly1305 o GCM, vuelva a calcular el MAC del texto cifrado y compárelo con el MAC que se envió utilizando hash_equals() . Si falla, abortar.
  2. Descifrar el mensaje.

Otras consideraciones de diseño:

  1. No compres nada nunca. El texto cifrado no es compresible; la compresión de texto sin formato antes del cifrado puede provocar fugas de información (por ejemplo, CRIME y BREACH en TLS).
  2. Asegúrese de usar mb_strlen() y mb_substr(), usando el modo de juego de caracteres '8bit' para evitar los problemas de mbstring.func_overload.
  3. Los IV deben generarse utilizando un CSPRNG ; Si está utilizando mcrypt_create_iv(), NO USE MCRYPT_Rand!
  4. A menos que esté utilizando una construcción AEAD, SIEMPRE encripte, luego MAC!
  5. bin2hex(), base64_encode(), etc. puede filtrar información sobre sus claves de cifrado a través del tiempo de caché. Evítalos si es posible.

Incluso si sigue los consejos dados aquí, muchas cosas pueden ir mal con la criptografía. Siempre haga que un experto en criptografía revise su implementación.Si no tiene la suerte de ser amigos personales con un estudiante de criptografía en su universidad local, siempre puede probar el foro Cryptography Stack Exchange para obtener consejos.

Si necesita un análisis profesional de su implementación, siempre puede contratar un equipo de asesores de seguridad acreditados para revisar su PHP código de criptografía (divulgación: mi empleador).

Importante: cuándo no usar cifrado

Don't cifrar contraseñas. En su lugar, desea hash _ usar uno de estos algoritmos de hashing de contraseñas:

Nunca use una función hash de propósito general (MD5, SHA256) para el almacenamiento de contraseñas.

No cifrar los parámetros de URL .Es la herramienta incorrecta para el trabajo.

Ejemplo de cifrado de cadena PHP con Libsodium

Si está en PHP <7.2 o, de lo contrario, no tiene instalado libsodium, puede usar sodium_compat para lograr el mismo resultado (aunque más lento).

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

Entonces para probarlo:

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

Halita - Libsodium Made Easy

Uno de los proyectos en los que he estado trabajando es una biblioteca de cifrado llamada Halite , cuyo objetivo es hacer que libsodium sea más fácil y más intuitivo.

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

Toda la criptografía subyacente es manejada por libsodium.

Ejemplo con cifrado/desactivación php

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

Nota: Crypto::encrypt() devuelve salida codificada en hexadecimal.

Gestión de claves de cifrado

Si está tentado a usar una "contraseña", deténgase ahora mismo. Necesita una clave de cifrado aleatoria de 128 bits, no una contraseña humana memorable.

Puede almacenar una clave de cifrado para uso a largo plazo de la siguiente manera:

$storeMe = bin2hex($key);

Y, a petición, puede recuperarlo así:

$key = hex2bin($storeMe);

I fuertementerecomiendo simplemente almacenar una clave generada aleatoriamente para uso a largo plazo en lugar de cualquier tipo de contraseña como la clave (o para derivar la clave).

Si estás usando la biblioteca de Defuse:

"Pero yo realmente quiero usar una contraseña".

Esa es una mala idea, pero está bien, aquí está cómo hacerlo de manera segura.

Primero, genere una clave aleatoria y guárdela en una 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");

¡Tenga en cuenta que está agregando trabajo extra y podría usar esta constante como la clave y ahorrarse mucho dolor!

Luego use PBKDF2 (así) para obtener una clave de cifrado adecuada de su contraseña en lugar de cifrarla directamente con su contraseña.

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

No solo use una contraseña de 16 caracteres. Tu clave de cifrado será cómicamente rota.

362
Scott Arciszewski

Llego tarde a la fiesta, pero al buscar la forma correcta de hacerlo encontré esta página, fue uno de los mejores resultados de búsqueda de Google, por lo que me gustaría compartir mi opinión sobre el problema, que considero que es actualizado al momento de escribir este post (a partir de 2017). Desde PHP 7.1.0, el mcrypt_decrypt y mcrypt_encrypt serán obsoletos, por lo que la construcción del código de prueba futuro debe usar openssl_encrypt y openssl_decrypt

Puedes hacer algo como:

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

Importante : Esto utiliza Modo ECB , que no es seguro. Si desea una solución simple sin tomar un curso intensivo en ingeniería de criptografía, no lo escriba, solo use una biblioteca .

También puede utilizar cualquier otro método chipper, dependiendo de su necesidad de seguridad. Para conocer los métodos de chipper disponibles, consulte la función openssl_get_cipher_methods .

46
Emil Borconi

Para Laravel framework

Si está utilizando Laravel framework, entonces es más fácil cifrar y descifrar con funciones internas.

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

Nota: asegúrese de establecer una cadena aleatoria de 16, 24 o 32 caracteres en la opción de clave del archivo config/app.php. De lo contrario, los valores encriptados no serán seguros.

12
Somnath Muluk

Nota histórica: Esto fue escrito en el momento de PHP4. Esto es lo que llamamos "código heredado" ahora.

He dejado esta respuesta para propósitos históricos, pero algunos de los métodos ahora están en desuso, DES el método de encriptación no es una práctica recomendada, etc.

No he actualizado este código por dos razones: 1) Ya no trabajo con métodos de cifrado a mano en PHP, y 2) este código todavía tiene el propósito para el que estaba destinado: demostrar el concepto mínimo y simplista de cómo puede funcionar el cifrado. en PHP.

Si encuentra un tipo de fuente "encriptación PHP para dummies" simplista similar que puede hacer que la gente comience en 10 a 20 líneas de código o menos, hágamelo saber en los comentarios.

Más allá de eso, disfrute este episodio clásico de la respuesta de cifrado minimalista PHP4 de la era temprana.


Lo ideal es que tenga, o pueda obtener, acceso a la biblioteca mcrypt PHP, ya que es ciertamente popular y muy útil para una variedad de tareas. A continuación se detallan los diferentes tipos de cifrado y algunos códigos de ejemplo: Técnicas de cifrado 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 ); 
?> 

Algunas advertencias:

1) Nunca use un cifrado reversible o "simétrico" cuando un hash unidireccional funcionará.

2) Si los datos son realmente confidenciales, como números de tarjetas de crédito o de seguridad social, deténgase; necesita más de lo que cualquier simple código proporcionará, sino que necesita una biblioteca de cifrado diseñada para este propósito y una cantidad significativa de tiempo para investigar los métodos necesarios. Además, el software crypto es probablemente <10% de la seguridad de los datos confidenciales. Es como volver a cablear una estación de energía nuclear: acepta que la tarea es peligrosa y difícil y más allá de tu conocimiento si ese es el caso. Las multas financieras pueden ser inmensas, por lo que es mejor utilizar un servicio y enviarles la responsabilidad.

3) Cualquier tipo de cifrado fácilmente implementable, como se indica aquí, puede proteger razonablemente la información ligeramente importante que usted quiere evitar de miradas indiscretas o limitar la exposición en caso de fuga accidental o intencional. Pero viendo cómo la clave se almacena en texto sin formato en el servidor web, si pueden obtener los datos, pueden obtener la clave de descifrado.

Sea como sea, diviértete :)

6
BrianH

Si no desea utilizar la biblioteca (que debería), use algo como esto (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);
3
Ascon