tutoriales.com

Asegurando tus Datos: Cifrado y Descifrado en PHP para Proteger la Información Sensible

Este tutorial te guiará a través de los fundamentos del cifrado y descifrado de datos en PHP utilizando la extensión OpenSSL. Aprenderás a proteger información sensible con ejemplos prácticos, asegurando la confidencialidad y la integridad de tus aplicaciones.

Intermedio20 min de lectura6 views
Reportar error

🚀 Introducción al Cifrado en PHP

En la era digital actual, la protección de la información sensible es más crucial que nunca. Desde datos de usuarios hasta credenciales de acceso, la seguridad es una preocupación primordial para cualquier desarrollador. PHP, a través de su potente extensión OpenSSL, nos proporciona las herramientas necesarias para implementar soluciones de cifrado robustas y proteger nuestros datos.

Este tutorial te sumergirá en el mundo del cifrado simétrico y asimétrico en PHP, centrándose en el uso de la librería OpenSSL. Aprenderás a cifrar y descifrar cadenas de texto, archivos y entenderás las mejores prácticas para manejar claves de cifrado de forma segura.

¿Por qué Cifrar Datos?

El cifrado es el proceso de transformar información (texto plano) en un formato ilegible (texto cifrado) para evitar el acceso no autorizado. Solo aquellos con la clave correcta pueden descifrar y acceder a la información original. Las razones principales para cifrar son:

  • Confidencialidad: Garantiza que solo las partes autorizadas puedan leer la información.
  • Integridad: Ayuda a detectar si los datos han sido alterados durante la transmisión o almacenamiento.
  • Autenticación: Puede verificar la identidad del remitente.

🔑 Fundamentos del Cifrado y Conceptos Clave

Antes de sumergirnos en el código, es fundamental comprender algunos conceptos clave.

📌 Nota: El cifrado es un campo complejo. Este tutorial se enfoca en la implementación práctica, pero siempre es bueno profundizar en la teoría criptográfica.

Cifrado Simétrico vs. Asimétrico

Existen dos tipos principales de cifrado:

  1. Cifrado Simétrico: Utiliza una única clave tanto para cifrar como para descifrar la información. Es rápido y eficiente, ideal para grandes volúmenes de datos. Ejemplos: AES (Advanced Encryption Standard), DES.
  2. Cifrado Asimétrico: Utiliza un par de claves: una clave pública para cifrar y una clave privada para descifrar. La clave pública se puede compartir libremente, mientras que la clave privada debe mantenerse en secreto. Es más lento, pero permite un intercambio seguro de claves y firmas digitales. Ejemplos: RSA.

En este tutorial nos centraremos principalmente en el cifrado simétrico con AES, ya que es el más común para proteger datos específicos en aplicaciones.

¿Cuándo usar cada tipo?
  • **Simétrico:** Cifrado de bases de datos, almacenamiento de sesiones sensibles, comunicación dentro de un mismo sistema.
  • **Asimétrico:** Establecimiento de conexiones seguras (HTTPS), firmas digitales, intercambio inicial de claves simétricas.

Términos Importantes

  • Texto Plano (Plaintext): La información original sin cifrar.
  • Texto Cifrado (Ciphertext): La información después de ser cifrada, ilegible sin la clave.
  • Clave (Key): Una cadena secreta de bits utilizada en el proceso de cifrado y descifrado.
  • Vector de Inicialización (IV - Initialization Vector): Un valor aleatorio que se utiliza junto con la clave para cifrar datos. Asegura que el mismo texto plano, cifrado múltiples veces con la misma clave, produzca textos cifrados diferentes. Es crucial para la seguridad y no necesita ser secreto, pero debe ser único para cada operación de cifrado.
  • Algoritmo de Cifrado (Cipher Algorithm): El método matemático utilizado para cifrar y descifrar los datos (ej. aes-256-cbc).

🛠️ Configuración y Requisitos

Para seguir este tutorial, necesitarás:

  • PHP 7.2 o superior: Las funciones de OpenSSL han mejorado con las versiones.
  • Extensión OpenSSL habilitada: Por lo general, está habilitada por defecto en la mayoría de las instalaciones de PHP. Puedes verificarlo con phpinfo() o ejecutando php -m | grep openssl en tu terminal.

Si no está habilitada, busca en tu php.ini la línea extension=openssl y descoméntala, o instálala según tu sistema operativo (sudo apt-get install php-openssl en Debian/Ubuntu).

🔒 Cifrado y Descifrado Simétrico con OpenSSL en PHP

La función principal para el cifrado simétrico en PHP es openssl_encrypt() y para el descifrado openssl_decrypt().

1. Generando una Clave Segura

La seguridad de tu cifrado depende directamente de la seguridad de tu clave. Nunca uses claves estáticas y fáciles de adivinar. Debes generar claves criptográficamente seguras.

<?php

// Generar una clave de 256 bits (32 bytes) para AES-256
// openssl_random_pseudo_bytes es preferible a random_bytes para compatibilidad en algunos entornos, 
// pero random_bytes es criptográficamente seguro y preferido si está disponible.

// Usando random_bytes (PHP 7+)
$key = random_bytes(32); // 32 bytes = 256 bits
echo 'Clave generada (raw): ' . $key . "\n";
echo 'Clave generada (hex): ' . bin2hex($key) . "\n";

// Si necesitas una clave de 128 bits (16 bytes) para AES-128
// $key = random_bytes(16);

// Almacenar la clave de forma segura: en variables de entorno, un servicio de gestión de secretos, etc.
// NUNCA hardcodees la clave en tu código ni la guardes en un archivo de texto plano accesible.

?>
⚠️ Advertencia: NUNCA almacenes tus claves de cifrado directamente en el código fuente o en un archivo en el mismo directorio que tu aplicación. Considera variables de entorno, un servicio de gestión de secretos (Vault, AWS KMS, Azure Key Vault), o un archivo fuera del directorio web con permisos restrictivos.

2. Seleccionando un Algoritmo de Cifrado

Es crucial elegir un algoritmo moderno y seguro. aes-256-cbc es una excelente opción ampliamente aceptada. El cbc (Cipher Block Chaining) es un modo de operación que aumenta la seguridad al encadenar bloques de cifrado, requiriendo un IV.

Para ver los algoritmos disponibles:

<?php

print_r(openssl_get_cipher_methods());

// Buscar el que nos interesa
if (in_array('aes-256-cbc', openssl_get_cipher_methods())) {
    echo "\naes-256-cbc está disponible.\n";
} else {
    echo "\naes-256-cbc NO está disponible. Considera actualizar OpenSSL o PHP.\n";
}

?>

3. Generando el Vector de Inicialización (IV)

El IV debe ser único y aleatorio para cada operación de cifrado, pero no necesita ser secreto. Se transmite junto con el texto cifrado. La longitud del IV depende del algoritmo.

<?php

$cipher = 'aes-256-cbc';
$ivlen = openssl_cipher_iv_length($cipher);
$iv = openssl_random_pseudo_bytes($ivlen); // Genera un IV aleatorio de la longitud correcta

echo 'Longitud del IV para ' . $cipher . ': ' . $ivlen . " bytes\n";
echo 'IV generado (raw): ' . $iv . "\n";
echo 'IV generado (hex): ' . bin2hex($iv) . "\n";

?>
🔥 Importante: La generación del IV debe ser **criptográficamente segura**. Usa `openssl_random_pseudo_bytes()` o `random_bytes()` (a partir de PHP 7).

4. Cifrando Datos

Ahora combinamos todo para cifrar una cadena de texto.

<?php

$cipher = 'aes-256-cbc';
$key = random_bytes(32); // Clave de 256 bits
$ivlen = openssl_cipher_iv_length($cipher);
$iv = openssl_random_pseudo_bytes($ivlen);

$plaintext = "Este es un mensaje secreto que quiero proteger.";

// Cifrar el texto plano
$ciphertext_raw = openssl_encrypt($plaintext, $cipher, $key, $options=OPENSSL_RAW_DATA, $iv);

// Combinar IV y ciphertext y codificar a Base64 para almacenar o transmitir fácilmente
// Es crucial que el IV se transmita junto con el texto cifrado para poder descifrarlo.
$ciphertext = base64_encode( $iv . $ciphertext_raw );

echo 'Texto Plano: ' . $plaintext . "\n";
echo 'Texto Cifrado (Base64): ' . $ciphertext . "\n";

// Para fines de depuración, puedes ver la clave y el IV (no hagas esto en producción)
// echo 'Clave (hex): ' . bin2hex($key) . "\n";
// echo 'IV (hex): ' . bin2hex($iv) . "\n";

?>

Explicación de openssl_encrypt() parámetros:

  • $data: La cadena a cifrar.
  • $cipher_algo: El algoritmo de cifrado (ej. aes-256-cbc).
  • $key: La clave secreta.
  • $options: OPENSSL_RAW_DATA indica que el resultado debe ser binario crudo. Es lo recomendado para luego codificar a Base64. Otros valores son OPENSSL_ZERO_PAD o OPENSSL_PKCS1_PADDING si no usas OPENSSL_RAW_DATA.
  • $iv: El vector de inicialización.

5. Descifrando Datos

Para descifrar, necesitamos la misma clave y el mismo IV que se usaron para cifrar.

<?php

$cipher = 'aes-256-cbc';
$key = random_bytes(32); // Misma clave que se usó para cifrar
$ivlen = openssl_cipher_iv_length($cipher);
$iv = openssl_random_pseudo_bytes($ivlen); // Mismo IV que se usó para cifrar

$plaintext_original = "Este es un mensaje secreto que quiero proteger.";

// Ciframos para tener un texto cifrado de ejemplo
$ciphertext_raw = openssl_encrypt($plaintext_original, $cipher, $key, $options=OPENSSL_RAW_DATA, $iv);
$ciphertext_encoded = base64_encode( $iv . $ciphertext_raw ); // Texto cifrado codificado

// --- Proceso de Descifrado --- //

// 1. Decodificar de Base64
$ciphertext_decoded = base64_decode($ciphertext_encoded);

// 2. Extraer el IV (los primeros $ivlen bytes son el IV)
$iv_from_ciphertext = substr($ciphertext_decoded, 0, $ivlen);

// 3. Extraer el texto cifrado sin el IV
$ciphertext_only = substr($ciphertext_decoded, $ivlen);

// 4. Descifrar el texto
$decrypted_plaintext = openssl_decrypt($ciphertext_only, $cipher, $key, $options=OPENSSL_RAW_DATA, $iv_from_ciphertext);

echo 'Texto Cifrado recibido (Base64): ' . $ciphertext_encoded . "\n";
echo 'Texto Descifrado: ' . $decrypted_plaintext . "\n";

// Verificar si el descifrado fue exitoso
if ($decrypted_plaintext === $plaintext_original) {
    echo "✅ ¡El descifrado fue exitoso y el texto coincide!\n";
} else {
    echo "❌ Error al descifrar o el texto no coincide.\n";
}

?>

Explicación de openssl_decrypt() parámetros:

  • $data: La cadena cifrada (raw, sin Base64).
  • $cipher_algo: El algoritmo de cifrado (debe ser el mismo que se usó para cifrar).
  • $key: La clave secreta (debe ser la misma).
  • $options: Las mismas opciones que se usaron en el cifrado (ej. OPENSSL_RAW_DATA).
  • $iv: El vector de inicialización (debe ser el mismo).
Texto Plano Generar Clave Generar IV Elegir Algoritmo openssl_encrypt() Texto Cifrado (binario) Combinar con IV + Codificar Base64 Texto Cifrado (Base64) Decodificar y separar IV openssl_decrypt() Clave Texto Descifrado

📦 Encapsulando el Cifrado: Una Clase Auxiliar

Para facilitar el uso y mantener la consistencia, es una buena práctica encapsular las operaciones de cifrado y descifrado en una clase o conjunto de funciones.

<?php

class DataProtector
{
    private string $key;
    private string $cipher = 'aes-256-cbc';

    public function __construct(string $key)
    {
        // Asegurarse de que la clave tenga la longitud correcta para el cifrado elegido
        // Para aes-256-cbc, la clave debe ser de 32 bytes (256 bits)
        if (mb_strlen($key, '8bit') !== 32) {
            throw new InvalidArgumentException("La clave debe tener 32 bytes de longitud para aes-256-cbc.");
        }
        $this->key = $key;
    }

    /**
     * Cifra un string dado.
     * @param string $plaintext El texto a cifrar.
     * @return string El texto cifrado en Base64, incluyendo el IV.
     * @throws Exception Si no se puede cifrar.
     */
    public function encrypt(string $plaintext): string
    {
        $ivlen = openssl_cipher_iv_length($this->cipher);
        $iv = openssl_random_pseudo_bytes($ivlen);

        if ($iv === false) {
            throw new Exception("No se pudo generar un IV seguro.");
        }

        $ciphertext_raw = openssl_encrypt($plaintext, $this->cipher, $this->key, OPENSSL_RAW_DATA, $iv);

        if ($ciphertext_raw === false) {
            throw new Exception("No se pudo cifrar los datos: " . openssl_error_string());
        }

        // Combina el IV y el texto cifrado, luego codifica a Base64
        return base64_encode($iv . $ciphertext_raw);
    }

    /**
     * Descifra un string cifrado previamente.
     * @param string $ciphertext_encoded El texto cifrado en Base64.
     * @return string El texto plano descifrado.
     * @throws Exception Si no se puede descifrar o el formato es inválido.
     */
    public function decrypt(string $ciphertext_encoded): string
    {
        $ciphertext_decoded = base64_decode($ciphertext_encoded);

        if ($ciphertext_decoded === false) {
            throw new Exception("El texto cifrado no es una cadena Base64 válida.");
        }

        $ivlen = openssl_cipher_iv_length($this->cipher);

        if (mb_strlen($ciphertext_decoded, '8bit') < $ivlen) {
            throw new Exception("El texto cifrado es demasiado corto para contener un IV.");
        }

        $iv = mb_substr($ciphertext_decoded, 0, $ivlen, '8bit');
        $ciphertext_only = mb_substr($ciphertext_decoded, $ivlen, null, '8bit');

        $plaintext = openssl_decrypt($ciphertext_only, $this->cipher, $this->key, OPENSSL_RAW_DATA, $iv);

        if ($plaintext === false) {
            throw new Exception("No se pudo descifrar los datos: " . openssl_error_string());
        }

        return $plaintext;
    }

    /**
     * Genera una clave segura de 32 bytes (256 bits).
     * @return string Clave binaria segura.
     * @throws Exception Si no se puede generar la clave.
     */
    public static function generateKey(): string
    {
        $key = random_bytes(32); // 32 bytes = 256 bits
        if ($key === false) {
            throw new Exception("No se pudo generar una clave segura.");
        }
        return $key;
    }

    /**
     * Convierte una clave binaria a formato hexadecimal para almacenamiento/visualización segura.
     * @param string $key Clave binaria.
     * @return string Clave en formato hexadecimal.
     */
    public static function keyToHex(string $key): string
    {
        return bin2hex($key);
    }

    /**
     * Convierte una clave hexadecimal a formato binario.
     * @param string $hexKey Clave en formato hexadecimal.
     * @return string Clave binaria.
     */
    public static function hexToKey(string $hexKey): string
    {
        return hex2bin($hexKey);
    }
}

// --- Ejemplo de Uso --- //

try {
    // Genera una clave segura (deberías hacer esto una vez y almacenarla de forma segura)
    $secretKey = DataProtector::generateKey();
    // Para demostración, la convertimos a hex y luego de vuelta (no necesario si se almacena binaria)
    // $secretKeyHex = DataProtector::keyToHex($secretKey);
    // $secretKey = DataProtector::hexToKey($secretKeyHex);

    // Inicializa el protector de datos con tu clave secreta
    $protector = new DataProtector($secretKey);

    $dataToEncrypt = "Mi número de tarjeta de crédito es 1234-5678-9012-3456 y mi CVV es 123.";

    // Cifra los datos
    $encryptedData = $protector->encrypt($dataToEncrypt);
    echo "\nDatos originales: " . $dataToEncrypt . "\n";
    echo "Datos cifrados (Base64): " . $encryptedData . "\n";

    // Descifra los datos
    $decryptedData = $protector->decrypt($encryptedData);
    echo "Datos descifrados: " . $decryptedData . "\n";

    if ($dataToEncrypt === $decryptedData) {
        echo "✅ ¡Cifrado y descifrado exitosos!\n";
    } else {
        echo "❌ Error: Los datos descifrados no coinciden con los originales.\n";
    }

    // Intentar con una clave incorrecta para ver el fallo
    $wrongKey = random_bytes(32);
    $wrongProtector = new DataProtector($wrongKey);
    try {
        $wrongDecryption = $wrongProtector->decrypt($encryptedData);
        echo "\nDescifrado con clave incorrecta: " . $wrongDecryption . "\n";
    } catch (Exception $e) {
        echo "\n❌ Error al descifrar con clave incorrecta (esperado): " . $e->getMessage() . "\n";
    }

} catch (Exception $e) {
    echo "Se produjo un error: " . $e->getMessage() . "\n";
}

?>

📂 Cifrado y Descifrado de Archivos

A veces, necesitas proteger archivos completos. El principio es el mismo, pero trabajamos con flujos de datos.

<?php

$cipher = 'aes-256-cbc';
$key = random_bytes(32); // Clave de 256 bits

$sourceFile = 'documento_secreto.txt';
$encryptedFile = 'documento_secreto.enc';
$decryptedFile = 'documento_secreto_decrypted.txt';

// Crear un archivo de prueba
file_put_contents($sourceFile, "Contenido altamente confidencial de mi documento secreto.\n");
file_put_contents($sourceFile, "Otra línea de texto importante.\n", FILE_APPEND);

echo "\nContenido original del archivo: " . file_get_contents($sourceFile) . "\n";

try {
    // --- Cifrado de Archivo ---
    $ivlen = openssl_cipher_iv_length($cipher);
    $iv = openssl_random_pseudo_bytes($ivlen);

    if ($iv === false) {
        throw new Exception("No se pudo generar un IV seguro.");
    }

    $inFileHandle = fopen($sourceFile, 'rb');
    $outFileHandle = fopen($encryptedFile, 'wb');

    if ($inFileHandle === false || $outFileHandle === false) {
        throw new Exception("No se pudo abrir los archivos.");
    }

    // Escribir el IV al principio del archivo cifrado
    fwrite($outFileHandle, $iv);

    // Crear un filtro de cifrado
    stream_filter_append($outFileHandle, 'convert.base64-encode', STREAM_FILTER_WRITE);
    $filter = stream_filter_append($outFileHandle, 'mcrypt.aes-256-cbc', STREAM_FILTER_WRITE, ['key' => $key, 'iv' => $iv]);

    // Nota: 'mcrypt' ha sido reemplazado por OpenSSL, pero para stream_filter_append, 
    // a menudo se sigue usando el nombre 'mcrypt.aes-256-cbc' para el filtro, incluso si el backend es OpenSSL.
    // En PHP 7.1+, stream_filter_append con 'mcrypt.*' a menudo delega a OpenSSL si mcrypt no está disponible/activo.
    // Para un uso más directo con OpenSSL, se podría leer en bloques y cifrar manualmente.

    if ($filter === false) {
        throw new Exception("No se pudo crear el filtro de cifrado. Asegúrate de que OpenSSL esté habilitado.");
    }

    while (!feof($inFileHandle)) {
        $chunk = fread($inFileHandle, 8192); // Leer en chunks
        fwrite($outFileHandle, $chunk);
    }

    fclose($inFileHandle);
    fclose($outFileHandle);

    echo "Archivo cifrado guardado en: " . $encryptedFile . "\n";

    // --- Descifrado de Archivo ---
    $inFileHandleEnc = fopen($encryptedFile, 'rb');
    $outFileHandleDec = fopen($decryptedFile, 'wb');

    if ($inFileHandleEnc === false || $outFileHandleDec === false) {
        throw new Exception("No se pudo abrir los archivos para descifrar.");
    }

    // Leer el IV del principio del archivo cifrado
    $iv_from_file = fread($inFileHandleEnc, $ivlen);
    if (mb_strlen($iv_from_file, '8bit') !== $ivlen) {
        throw new Exception("IV inválido o archivo corrupto.");
    }

    // Crear un filtro de descifrado
    $filterDec = stream_filter_append($outFileHandleDec, 'mcrypt.aes-256-cbc', STREAM_FILTER_WRITE, ['key' => $key, 'iv' => $iv_from_file]);
    stream_filter_append($outFileHandleDec, 'convert.base64-decode', STREAM_FILTER_WRITE);

    if ($filterDec === false) {
        throw new Exception("No se pudo crear el filtro de descifrado.");
    }

    while (!feof($inFileHandleEnc)) {
        $chunk = fread($inFileHandleEnc, 8192); // Leer en chunks del archivo cifrado
        fwrite($outFileHandleDec, $chunk);
    }

    fclose($inFileHandleEnc);
    fclose($outFileHandleDec);

    echo "Archivo descifrado guardado en: " . $decryptedFile . "\n";

    if (file_get_contents($sourceFile) === file_get_contents($decryptedFile)) {
        echo "✅ Archivo cifrado y descifrado correctamente. El contenido coincide.\n";
    } else {
        echo "❌ Error: El contenido del archivo descifrado no coincide con el original.\n";
    }

} catch (Exception $e) {
    echo "Error en el cifrado/descifrado de archivos: " . $e->getMessage() . "\n";
}

// Limpiar archivos de prueba
unlink($sourceFile);
unlink($encryptedFile);
unlink($decryptedFile);

?>
⚠️ Advertencia: El uso de `stream_filter_append` con nombres como `mcrypt.*` puede ser confuso y depende de la configuración de tu PHP. Para OpenSSL puro, a menudo es más robusto leer/escribir en bloques y usar `openssl_encrypt`/`openssl_decrypt` en cada bloque manualmente, gestionando el *padding* si es necesario. El ejemplo anterior es simplificado.

✨ Mejores Prácticas de Seguridad

Implementar el cifrado correctamente va más allá de solo llamar a las funciones. Aquí tienes algunas mejores prácticas:

  • Gestión de Claves:
    • Generación: Utiliza funciones criptográficamente seguras (random_bytes, openssl_random_pseudo_bytes).
    • Almacenamiento: Las claves deben almacenarse de forma extremadamente segura. Nunca en el código, en el control de versiones, o en archivos accesibles públicamente. Usa variables de entorno, un servicio de gestión de secretos (AWS KMS, Azure Key Vault, HashiCorp Vault), o hardware de seguridad (HSM).
    • Rotación: Rota tus claves de cifrado periódicamente (ej. cada año) o cuando haya un incidente de seguridad.
  • IV Único: Asegúrate de que cada operación de cifrado utilice un IV nuevo y aleatorio. Es permisible almacenar el IV junto con el texto cifrado.
  • Algoritmos Modernos: Usa algoritmos como AES-256-CBC o AES-GCM. Evita algoritmos antiguos o considerados débiles (DES, MD5 para hashing, etc.). AES-GCM es preferido porque proporciona autenticación adicional (garantiza la integridad).
  • Protección contra Manipulación: Si la integridad es crucial, considera usar HMAC (Hash-based Message Authentication Code) junto con el cifrado simétrico, o utiliza un modo de cifrado autenticado como AES-GCM.
  • No Implementes Tu Propia Criptografía: A menos que seas un experto en criptografía, siempre usa bibliotecas y funciones estándar probadas como OpenSSL. Es increíblemente fácil introducir vulnerabilidades al intentar crear tu propio algoritmo.
  • Considera Bibliotecas de Alto Nivel: Para aplicaciones complejas, frameworks como Laravel ofrecen capas de abstracción para el cifrado que simplifican el proceso y aplican muchas de estas mejores prácticas por defecto.

AES-GCM: Cifrado Autenticado

AES-GCM (Galois/Counter Mode) es un modo de operación de AES que no solo cifra los datos sino que también proporciona autenticación y protección de la integridad a través de una etiqueta de autenticación (Authentication Tag - tag).

<?php

$cipher = 'aes-256-gcm'; // Algoritmo con GCM
$key = random_bytes(32); // Clave de 256 bits

$ivlen = openssl_cipher_iv_length($cipher); // Longitud del IV para GCM (típicamente 12 o 16 bytes)
$iv = random_bytes($ivlen);

$plaintext = "Este es un mensaje secreto que quiero proteger con GCM y su autenticación.";
$tag = ''; // Variable para almacenar la etiqueta de autenticación

// Cifrar con GCM
$ciphertext_gcm = openssl_encrypt($plaintext, $cipher, $key, OPENSSL_RAW_DATA, $iv, $tag);

// Combinar IV, tag y texto cifrado. La etiqueta también debe transmitirse.
$encryptedData_gcm = base64_encode($iv . $tag . $ciphertext_gcm);

echo "\nTexto original (GCM): " . $plaintext . "\n";
echo "Texto cifrado (GCM, Base64): " . $encryptedData_gcm . "\n";

// --- Descifrado con GCM ---
$ciphertext_decoded_gcm = base64_decode($encryptedData_gcm);

$iv_gcm = mb_substr($ciphertext_decoded_gcm, 0, $ivlen, '8bit');
$tag_gcm = mb_substr($ciphertext_decoded_gcm, $ivlen, 16, '8bit'); // La etiqueta GCM es de 16 bytes
$ciphertext_only_gcm = mb_substr($ciphertext_decoded_gcm, $ivlen + 16, null, '8bit');

// Descifrar con GCM. La 'tag' se pasa como referencia y se compara con $tag_gcm.
$decrypted_gcm = openssl_decrypt($ciphertext_only_gcm, $cipher, $key, OPENSSL_RAW_DATA, $iv_gcm, $tag_gcm);

if ($decrypted_gcm !== false) {
    echo "Texto descifrado (GCM): " . $decrypted_gcm . "\n";
    if ($decrypted_gcm === $plaintext) {
        echo "✅ ¡Cifrado y descifrado GCM exitoso!\n";
    } else {
        echo "❌ Error: Los datos descifrados GCM no coinciden.\n";
    }
} else {
    echo "❌ Error al descifrar con GCM o los datos fueron manipulados.\n";
    echo "Error: " . openssl_error_string() . "\n";
}

?>

Cuando openssl_decrypt con GCM devuelve false, significa que los datos fueron manipulados o la clave/IV/tag no son correctos, lo cual es una característica de seguridad muy potente.

📈 Rendimiento y Consideraciones

El cifrado y descifrado tienen un costo computacional. Para la mayoría de las aplicaciones web, el impacto es insignificante cuando se cifra solo una pequeña cantidad de datos (ej. tokens, contraseñas hash). Sin embargo, al cifrar grandes volúmenes de datos o archivos, el rendimiento puede ser una consideración.

  • Prueba de Rendimiento: Realiza pruebas de estrés si estás cifrando/descifrando datos en alta concurrencia o grandes archivos.
  • Hardware: Los servidores modernos suelen tener instrucciones de CPU especializadas (AES-NI) que aceleran las operaciones AES.
  • Almacenamiento: El texto cifrado siempre será ligeramente más grande que el texto plano debido al IV y la posible codificación (Base64).
Rendimiento para datos pequeños: 90%
Rendimiento para datos grandes: 60%

❓ Preguntas Frecuentes (FAQ)

¿Es seguro almacenar el IV junto al texto cifrado? Sí, el IV no necesita ser secreto. Su propósito es asegurar que el mismo texto plano se cifre de manera diferente cada vez. Lo crucial es que sea aleatorio y único para cada operación de cifrado.
¿Qué pasa si la clave se ve comprometida? Si la clave se ve comprometida, todos los datos cifrados con esa clave pueden ser descifrados. Por eso la gestión de claves es la parte más crítica del cifrado.
¿Debo cifrar las contraseñas de usuario con OpenSSL? No. Las contraseñas de usuario deben ser *hasheadas* con funciones de hash lentas y resistentes a ataques de fuerza bruta, como `password_hash()` con `PASSWORD_ARGON2ID` o `PASSWORD_BCRYPT`. Cifrar contraseñas implica que alguien podría descifrarlas si obtiene la clave. El hashing es un proceso unidireccional.
¿Se pueden cifrar grandes archivos con PHP? Sí, es posible, como se mostró con `stream_filter_append`. Sin embargo, para archivos muy grandes, considera soluciones a nivel de sistema operativo o librerías optimizadas para ello, o procesa los archivos en chunks más pequeños para no agotar la memoria. Para aplicaciones críticas, GCM es la elección por su integridad.

Conclusión ✅

El cifrado es una herramienta indispensable en el arsenal de cualquier desarrollador. Con la extensión OpenSSL de PHP, tienes el poder de proteger la información sensible de tus usuarios y aplicaciones de manera efectiva. Al seguir las mejores prácticas en la gestión de claves, la selección de algoritmos y el uso de IVs únicos, puedes construir sistemas más seguros y confiables.

Recuerda: la seguridad es un proceso continuo. Mantente al día con las últimas recomendaciones y vulnerabilidades en criptografía para asegurar que tus aplicaciones permanezcan protegidas.

Tutoriales relacionados

Comentarios (0)

Aún no hay comentarios. ¡Sé el primero!