En seguridad, “encriptar” no es lo mismo que “hashear”. Este manual resume cuándo usar cada cosa, cómo hacerlo correctamente en PHP y las buenas prácticas mínimas.
Para almacenar contraseñas no se encripta, se usa hash con sal y factor de costo.
En PHP: password_hash() y password_verify() (manejan sal y costo por vos).
<?php
$clavePlano = "Secreta.123";
// Crear hash (BCrypt/Argon2 según tu PHP)
$hash = password_hash($clavePlano, PASSWORD_DEFAULT);
// Guardá $hash en BD (no guardes la clave en texto)
echo $hash . "\n";
// Verificar al loguear:
$ok = password_verify("Secreta.123", $hash);
echo $ok ? "✅ Coincide" : "❌ No coincide";
?>
Tips: Usá PASSWORD_DEFAULT y permití rehash con password_needs_rehash() si cambia el costo/algoritmo.
Para datos que necesitás recuperar (ej. DNI cifrado, tokens temporales), usá encriptación simétrica. Con OpenSSL: elegí AES-256 en modo AEAD (GCM) para confidencialidad + integridad.
<?php $plaintext = "Dato sensible"; $key = random_bytes(32); // 256 bits (guardala segura) $cipher = "aes-256-gcm"; // IV/nonce único por mensaje (12 bytes recomendado para GCM) $iv = random_bytes(12); $ciphertext = openssl_encrypt( $plaintext, $cipher, $key, OPENSSL_RAW_DATA, $iv, $tag // etiqueta de autenticación (16 bytes) ); // Luego, para transportar/guardar: base64 $paquete = base64_encode($iv . $tag . $ciphertext); echo $paquete; // ----- Decrypt ----- $raw = base64_decode($paquete); $iv2 = substr($raw, 0, 12); $tag2 = substr($raw, 12, 16); $ct2 = substr($raw, 28); $decrypted = openssl_decrypt( $ct2, $cipher, $key, OPENSSL_RAW_DATA, $iv2, $tag2 ); echo "\n" . $decrypted; // "Dato sensible" ?>
Clave: almacenala fuera del repositorio, en variables de entorno o un secret manager. IV/nonce: nuevo y único por mensaje; puede ir junto con el texto cifrado.
libsodium (extensión nativa) provee primitivas modernas y seguras por defecto.
Para cifrado autenticado simétrico, usá sodium_crypto_secretbox() (XSalsa20-Poly1305).
<?php
if (!extension_loaded('sodium')) {
die("Sodium no disponible");
}
$mensaje = "Secreto con sodium";
$key = random_bytes(SODIUM_CRYPTO_SECRETBOX_KEYBYTES); // 32 bytes
$nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES); // 24 bytes
$cipher = sodium_crypto_secretbox($mensaje, $nonce, $key);
// Empaquetar para transporte
$paquete = base64_encode($nonce . $cipher);
// ----- Decrypt -----
$raw = base64_decode($paquete);
$n2 = substr($raw, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
$c2 = substr($raw, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
$plain = sodium_crypto_secretbox_open($c2, $n2, $key);
if ($plain === false) {
die("Autenticación falló");
}
echo $plain; // "Secreto con sodium"
?>
Para cifrado asimétrico (emisor y receptor con claves diferentes), podés usar sodium_crypto_box().
Para claves, IV, tokens o sales: no uses rand()/mt_rand(). Usá:
random_bytes(n) → bytes criptográficamente seguros.random_int(min,max) → enteros seguros (tokens numéricos).<?php $token = bin2hex(random_bytes(16)); // 32 hex chars $otp = random_int(100000, 999999); // 6 dígitos ?>
password_hash/password_verify.secretbox (sodium).rand()/mt_rand().