php

Cifrar archivos con PHP

Resumen

Vamos a cifrar y descifrar archivos con PHP de dos maneras; la primera usando una clave “general” y otra con una contraseña.

Para cifrar y descifrar vamos a usar la librería php-encryption.

La diferencia es que con el segundo ejemplo podremos hacer que los usuarios tengan sus archivos cifrados con una contraseña que solo ellos sepan, y cada uno tendrá una clave distinta.

Ejemplo de uso de php-encryption

En un post anterior vimos cómo encriptar texto o datos como strings / variables en PHP.

Ahora vamos a ver cómo cifrar o encriptar archivos completos con PHP; es decir, no encriptar texto, sino archivos como imágenes, vídeos, documentos, etcétera.

Instalar librería php-encryption

Vamos a usar la misma librería que en el post citado. Si no la has instalado, ejecuta:

composer require defuse/php-encryption

En caso de que Composer no esté instalado mira cómo instalar Composer aquí; y si quieres adaptar tu proyecto puedes ver un tutorial aquí.

Nota: si quieres comprobar que estás descargando el archivo real y que nadie lo interceptó en el camino puedes descargar el archivo phar y comprobar las firmas.

Después de instalarla simplemente hay que requerir el autoload como veremos en los ejemplos.

require_once "vendor/autoload.php";

Cifrar con clave general

Recuerda que la clave puede ser generada con el ejecutable llamado generate-defuse-key ubicado en vendor/bin.

La clave debe estar guardada en un lugar seguro al que solo tú tengas acceso, pues quien tenga acceso podrá cifrar y descifrar a su gusto.

<?php
/**
 * Cifrar un archivo con php-encryption
 *
 * https://parzibyte.me/blog
 *
 */require_once "vendor/autoload.php";

use Defuse\Crypto\Exception\EnvironmentIsBrokenException;
use Defuse\Crypto\Exception\IOException;
use Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException;
use Defuse\Crypto\File;
use Defuse\Crypto\Key;

// Cualquier archivo es válido

// El archivo que se va a cifrar
$rutaArchivoEntrada = __DIR__ . "/kotlin.png";
// El archivo de salida; es decir, el que estará cifrado
$rutaArchivoSalida = __DIR__ . "/kotlin.cifrado.png";
// No olvides guardar la clave en un lugar seguro; aquí lo pongo así de simple para ejemplos de
// simplicidad
$contenido = file_get_contents("clave.txt");
// Cargar la clave desde una cadena ASCII (pues la clave no es tan legible ni entendible como una simple cadena)
$clave = Key::loadFromAsciiSafeString($contenido);
// Y ya podemos cifrar el archivo
try {
    File::encryptFile($rutaArchivoEntrada, $rutaArchivoSalida, $clave);
    echo "Archivo $rutaArchivoEntrada cifrado dentro de $rutaArchivoSalida";
    // Opcionalmente podrías eliminar el original:
    # unlink($rutaArchivoEntrada);
    # El manejo de excepciones es opcional ;)
} catch (IOException $e) {
    echo "Error leyendo o escribiendo archivo. Verifica que el archivo de entrada exista y que tengas permiso de escritura";
} catch (EnvironmentIsBrokenException $e) {
    echo "El entorno está roto. Normalmente es porque la plataforma actual no puede encriptar el archivo de una manera segura";
} catch (WrongKeyOrModifiedCiphertextException $e) {
    echo "La clave es errónea o alguien la intentó modificar, cuidado";
}
def00000180bb725822d130c3e64f1c8c4544f468b91cc42b640c5d7a06a0eb0d3aa9cec0b9f44e407fbc674611f82b71555a9bb4c7f5a17ff9bd328eeb797a3fd288797

En el ejemplo de código estoy cifrando una imagen usando una clave que obtengo leyendo un archivo de texto.

El método File::encryptFile($rutaArchivoEntrada, $rutaArchivoSalida, $clave) toma la ruta de un archivo de entrada, lo cifra con la clave y el archivo cifrado lo pone en la ruta del archivo de salida.

Si quieres eliminar el archivo original simplemente usa unlink.

Descifrar con clave general

Ahora veamos cómo se podría descifrar el archivo cifrado anteriormente, igual con una clave.

def00000180bb725822d130c3e64f1c8c4544f468b91cc42b640c5d7a06a0eb0d3aa9cec0b9f44e407fbc674611f82b71555a9bb4c7f5a17ff9bd328eeb797a3fd288797
<?php
/**
 * Descifrar un archivo con php-encryption
 *
 * https://parzibyte.me/blog
 *
 */require_once "vendor/autoload.php";

use Defuse\Crypto\Exception\EnvironmentIsBrokenException;
use Defuse\Crypto\Exception\IOException;
use Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException;
use Defuse\Crypto\File;
use Defuse\Crypto\Key;

// Cualquier archivo es válido

// El archivo que se va a descifrar
$rutaArchivoEntrada = __DIR__ . "/kotlin.cifrado.png";
// El archivo de salida; es decir, el que estará descifrado
$rutaArchivoSalida = __DIR__ . "/kotlin.png";
// No olvides guardar la clave en un lugar seguro; aquí lo pongo así de simple para ejemplos de
// simplicidad
$contenido = file_get_contents("clave.txt");
// Cargar la clave desde una cadena ASCII (pues la clave no es tan legible ni entendible como una simple cadena)
$clave = Key::loadFromAsciiSafeString($contenido);
// Y ya podemos descifrar el archivo
try {
    File::decryptFile($rutaArchivoEntrada, $rutaArchivoSalida, $clave);
    echo "Archivo $rutaArchivoEntrada descifrado dentro de $rutaArchivoSalida";
    // Opcionalmente podrías eliminar el cifrado:
    # unlink($rutaArchivoEntrada);
    # El manejo de excepciones es opcional ;)
} catch (IOException $e) {
    echo "Error leyendo o escribiendo archivo. Verifica que el archivo de entrada exista y que tengas permiso de escritura";
} catch (EnvironmentIsBrokenException $e) {
    echo "El entorno está roto. Normalmente es porque la plataforma actual no puede encriptar el archivo de una manera segura";
} catch (WrongKeyOrModifiedCiphertextException $e) {
    echo "La clave es errónea o alguien la intentó modificar, cuidado";
}

Es muy similar al proceso para cifrar un archivo con PHP, solo que ahora el archivo de entrada es el archivo previamente encriptado; y se invoca al método decryptFile.

Lo digo de nuevo, para eliminar el archivo podrías usar unlink.

Cifrar con contraseña vs cifrar con clave

Existe otra manera de cifrar todo el contenido de un archivo con PHP y la librería php-encryption, y es usar una contraseña.

No te confundas, la clave y la contraseña son cosas distintas.

Una contraseña puede ser proporcionada por un usuario (así, cada usuario del sistema podría tener sus archivos seguros sin depender de una clave general)

La clave es una clave que podría decirse es especial y tiene un formato predefinido además de ser segura, y se usa cuando el sistema es de un solo usuario.

Cifrar con contraseña

Cifrar un archivo en PHP usando una contraseña es más fácil que encriptar un archivo usando la clave.

Ahora se invoca al método encryptFileWithPassword cuyo uso se ve en el siguiente código:

<?php
/**
 * Cifrar un archivo con php-encryption; usando una contraseña
 * en lugar de la clave
 *
 * https://parzibyte.me/blog
 *
 */require_once "vendor/autoload.php";

use Defuse\Crypto\Exception\EnvironmentIsBrokenException;
use Defuse\Crypto\Exception\IOException;
use Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException;
use Defuse\Crypto\File;

$password = "hunter2";

// Cualquier archivo es válido

// El archivo que se va a cifrar
$rutaArchivoEntrada = __DIR__ . "/script.js";
// El archivo de salida; es decir, el que estará cifrado
$rutaArchivoSalida = __DIR__ . "/script.cifrado.js";

try {
    File::encryptFileWithPassword($rutaArchivoEntrada, $rutaArchivoSalida, $password);
    echo "Archivo $rutaArchivoEntrada cifrado dentro de $rutaArchivoSalida";
    // Opcionalmente podrías eliminar el original:
    # unlink($rutaArchivoEntrada);
    # El manejo de excepciones es opcional ;)
} catch (IOException $e) {
    echo "Error leyendo o escribiendo archivo. Verifica que el archivo de entrada exista y que tengas permiso de escritura";
} catch (EnvironmentIsBrokenException $e) {
    echo "El entorno está roto. Normalmente es porque la plataforma actual no puede encriptar el archivo de una manera segura";
} catch (WrongKeyOrModifiedCiphertextException $e) {
    echo "La clave es errónea o alguien la intentó modificar, cuidado";
}

Pueden ocurrir excepciones, aunque el manejo de las mismas es opcional.

Como ves, encryptFileWithPassword toma la ruta del archivo original, lo cifra usando la contraseña y finalmente el contenido lo escribe en la ruta del archivo de salida.

Descifrar con contraseña

Para terminar el post veamos cómo descifrar o desencriptar un archivo con PHP usando una contraseña.

Recuerda que la misma contraseña que se utilizó para cifrar el contenido del archivo debe usarse para este proceso.

El código es el siguiente:

<?php
/**
 * Descifrar un archivo con php-encryption; usando una contraseña
 * en lugar de la clave
 *
 * https://parzibyte.me/blog
 *
 */require_once "vendor/autoload.php";

use Defuse\Crypto\Exception\EnvironmentIsBrokenException;
use Defuse\Crypto\Exception\IOException;
use Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException;
use Defuse\Crypto\File;

$password = "hunter2";

// Cualquier archivo es válido

// El archivo que se va a descifrar
$rutaArchivoEntrada = __DIR__ . "/script.cifrado.js";
// El archivo de salida; es decir, el que estará descifrado
$rutaArchivoSalida = __DIR__ . "/script.js";

try {
    File::decryptFileWithPassword($rutaArchivoEntrada, $rutaArchivoSalida, $password);
    echo "Archivo $rutaArchivoEntrada descifrado dentro de $rutaArchivoSalida";
    // Opcionalmente podrías eliminar el encriptado:
    # unlink($rutaArchivoEntrada);
    # El manejo de excepciones es opcional ;)
} catch (IOException $e) {
    echo "Error leyendo o escribiendo archivo. Verifica que el archivo de entrada exista y que tengas permiso de escritura";
} catch (EnvironmentIsBrokenException $e) {
    echo "El entorno está roto. Normalmente es porque la plataforma actual no puede encriptar el archivo de una manera segura";
} catch (WrongKeyOrModifiedCiphertextException $e) {
    echo "La clave es errónea o alguien la intentó modificar, cuidado";
}

Ahora el método es decryptFileWithPassword, el cual toma la ruta del archivo encriptado, la ruta en donde va a poner el archivo descifrado y la contraseña.

Conclusiones

Como lo dije en los ejemplos, después de cifrar o descifrar los archivos con PHP puedes eliminarlos usando la función unlink.

Por cierto, a los archivos les he puesto la extensión .cifrado para reconocerlos, pero el nombre puede ser cualquiera.

Los archivos que puedes cifrar con php-encryption son ilimitados, es decir, no hay una lista de extensiones o tipos.

Eso sí, recuerda que entre más grande sea el archivo, el proceso de cifrado y descifrado con PHP será más lento.

Nota: puedes ver el código en GitHub; los archivos de JavaScript e imágenes son para ilustrar el cifrado, no tienen nada que ver con este código.

Estoy aquí para ayudarte 🤝💻


Estoy aquí para ayudarte en todo lo que necesites. Si requieres alguna modificación en lo presentado en este post, deseas asistencia con tu tarea, proyecto o precisas desarrollar un software a medida, no dudes en contactarme. Estoy comprometido a brindarte el apoyo necesario para que logres tus objetivos. Mi correo es parzibyte(arroba)gmail.com, estoy como@parzibyte en Telegram o en mi página de contacto

No te pierdas ninguno de mis posts 🚀🔔

Suscríbete a mi canal de Telegram para recibir una notificación cuando escriba un nuevo tutorial de programación.
parzibyte

Programador freelancer listo para trabajar contigo. Aplicaciones web, móviles y de escritorio. PHP, Java, Go, Python, JavaScript, Kotlin y más :) https://parzibyte.me/blog/software-creado-por-parzibyte/

Entradas recientes

Creador de credenciales web – Aplicación gratuita

Hoy te voy a presentar un creador de credenciales que acabo de programar y que…

1 semana hace

Desplegar PWA creada con Vue 3, Vite y SQLite3 en Apache

Ya te enseñé cómo convertir una aplicación web de Vue 3 en una PWA. Al…

2 semanas hace

Arquitectura para wasm con Go, Vue 3, Pinia y Vite

En este artículo voy a documentar la arquitectura que yo utilizo al trabajar con WebAssembly…

2 semanas hace

Vue 3 y Vite: crear PWA (Progressive Web App)

En un artículo anterior te enseñé a crear un PWA. Al final, cualquier aplicación que…

2 semanas hace

Errores de Comlink y algunas soluciones

Al usar Comlink para trabajar con los workers usando JavaScript me han aparecido algunos errores…

2 semanas hace

Esperar promesa para inicializar Store de Pinia con Vue 3

En este artículo te voy a enseñar cómo usar un "top level await" esperando a…

2 semanas hace

Esta web usa cookies.