password_hash metodos encriptar encriptacion ejemplo contraseña php security encryption passwords

php - metodos - Cifrado bidireccional: necesito almacenar las contraseñas que se pueden recuperar



metodos de encriptacion php (8)

Las contraseñas son para un dispositivo de hardware, por lo que la comprobación contra hash está fuera de discusión

Eh? No entiendo. ¿Quiere decir que la contraseña debe ser recuperable?

Como han dicho otros, la extensión mcrypt proporciona acceso a muchas funciones criptográficas. Sin embargo, invitará a los usuarios a poner todos sus huevos en una sola cesta, que potencialmente será un objetivo para los atacantes, y si ni siquiera lo saben cómo comenzar a resolver el problema, entonces le está haciendo un flaco servicio a sus usuarios. No está en condiciones de entender cómo proteger los datos.

La mayoría de las vulnerabilidades de seguridad surgen no porque el algoritmo subyacente sea defectuoso o inseguro, sino por los problemas con la forma en que se usa el algoritmo dentro del código de la aplicación.

Habiendo dicho eso, es posible construir un sistema razonablemente seguro.

Solo debe considerar el cifrado asimétrico si necesita que un usuario cree un mensaje seguro que pueda leer otro usuario (específico). La razón es que es computacionalmente costoso. Si solo desea proporcionar un repositorio para que los usuarios ingresen y recuperen sus propios datos, el cifrado simétrico es adecuado.

Sin embargo, si almacena la clave para descifrar el mensaje en el mismo lugar que el mensaje cifrado (o donde se almacena el mensaje cifrado), entonces el sistema no es seguro. Use el mismo token para autenticar al usuario que para la clave de descifrado (o en el caso de cifrado asimétrico, use el token como la contraseña de clave privada). Como deberá almacenar el token en el servidor donde se realiza el descifrado al menos temporalmente, puede considerar utilizar un sustrato de almacenamiento de sesión que no se puede buscar, o pasar el token directamente a un daemon asociado con la sesión que almacenaría el token en la memoria y realizar el descifrado de los mensajes bajo demanda.

Estoy creando una aplicación que almacenará contraseñas, que el usuario puede recuperar y ver. Las contraseñas son para un dispositivo de hardware, por lo que no es posible verificar contra hash.

Lo que necesito saber es:

  1. ¿Cómo encripto y descifro una contraseña en PHP?

  2. ¿Cuál es el algoritmo más seguro para cifrar las contraseñas?

  3. ¿Dónde guardo la clave privada?

  4. En lugar de almacenar la clave privada, ¿es una buena idea solicitar a los usuarios que ingresen la clave privada cada vez que necesiten una contraseña descifrada? (Se puede confiar en los usuarios de esta aplicación)

  5. ¿De qué manera se puede robar y descifrar la contraseña? ¿De qué debo ser consciente?


  1. La función de PHP que está buscando es Mcrypt ( http://www.php.net/manual/en/intro.mcrypt.php ).

El ejemplo del manual está ligeramente editado para este ejemplo):

<?php $iv_size = mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_ECB); $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND); $key = "This is a very secret key"; $pass = "PasswordHere"; echo strlen($pass) . "/n"; $crypttext = mcrypt_encrypt(MCRYPT_BLOWFISH, $key, $pass, MCRYPT_MODE_ECB, $iv); echo strlen($crypttext) . "/n"; ?>

Utilizaría mcrypt_decrypt para descifrar su contraseña.

  1. El mejor algorithm es bastante subjetivo: pregunte a 5 personas, obtenga 5 respuestas. Personalmente, si el valor predeterminado (Blowfish) no es lo suficientemente bueno para ti, ¡probablemente tengas problemas más grandes!

  2. Dado que PHP lo necesita para cifrar, no estoy seguro de poder ocultarlo en ningún lado, bienvenidos los comentarios sobre esto. ¡Las mejores prácticas de codificación estándar de PHP se aplican por supuesto!

  3. Dado que la clave de cifrado estará en su código de todos modos, no está seguro de lo que obtendrá, siempre que el resto de la aplicación sea segura.

  4. Obviamente, si se roban la contraseña encriptada y la clave de cifrado, se termina el juego.

Pondría un jinete en mi respuesta: no soy un experto en criptografía PHP, pero creo que lo que he respondido es una práctica estándar. Recibo comentarios que otros pueden tener.


Intenté algo como esto, pero tenga en cuenta que no soy criptógrafo ni tengo un conocimiento profundo sobre php o cualquier lenguaje de programación. Es solo una idea Mi idea es almacenar la key en algún archivo o database (o ingresar manualmente) que (ubicación) no puede predecirse fácilmente (y por supuesto cualquier cosa será descifrada algún día, el concepto es alargar el tiempo de descifrado) y cifrar información confidencial.

$iv_size = mcrypt_get_iv_size(MCRYPT_BLOWFISH , MCRYPT_MODE_ECB); $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND); $key = "evenifyouaccessmydatabaseyouwillneverfindmyemail"; $text = "[email protected]"; echo "Key : ".$key."<br/>"; echo "Text : ".$text . "<br/>"; echo "Md5 : ".md5($text). "<br/>"; echo "Sha1 : ".sha1($text). "<br/>"; $crypttext = mcrypt_encrypt(MCRYPT_BLOWFISH , $key, $text, MCRYPT_MODE_ECB, $iv); echo "Crypted Data : ".$crypttext."<br>"; $base64 = base64_encode($crypttext); echo "Encoded Data : ".$base64."<br/>"; $decode = base64_decode($base64); $decryptdata = mcrypt_decrypt(MCRYPT_BLOWFISH , $key, $crypttext, MCRYPT_MODE_ECB, $iv); echo "Decoded Data : ".ereg_replace("?", null , $decryptdata); //event if i add ''?'' to the sting to the text it works, I don''t know why.

Tenga en cuenta que es solo un concepto. Cualquier mejora en este código sería altamente apreciable.


Muchos usuarios han sugerido usar mcrypt ... que es correcto, pero me gusta ir un paso más allá para que sea fácilmente almacenado y transferido (ya que a veces los valores cifrados pueden dificultar su envío usando otras tecnologías como curl o json) .

Después de haber cifrado correctamente con mcrypt, ejecútelo a través de base64_encode y luego conviértalo en código hexadecimal. Una vez dentro del código hexadecimal, es fácil transferirlo de varias maneras.

$td = mcrypt_module_open(''tripledes'', '''', ''ecb'', ''''); $iv = mcrypt_create_iv (mcrypt_enc_get_iv_size($td), MCRYPT_RAND); $key = substr("SUPERSECRETKEY",0,mcrypt_enc_get_key_size($td)); mcrypt_generic_init($td, $key, $iv); $encrypted = mcrypt_generic($td, $unencrypted); $encrypted = $ua."||||".$iv; mcrypt_generic_deinit($td); mcrypt_module_close($td); $encrypted = base64_encode($encrypted); $encrypted = array_shift(unpack(''H*'', $encrypted));

Y en el otro lado:

$encrypted = pack(''H*'', $encrypted); $encrypted = base64_decode($encrypted); list($encrypted,$iv) = explode("||||",$encrypted,2); $td = mcrypt_module_open(''tripledes'', '''', ''ecb'', ''''); $key = substr("SUPERSECRETKEY",0,mcrypt_enc_get_key_size($td)); mcrypt_generic_init($td, $key, $iv); $unencrypted = mdecrypt_generic($td, $encrypted); mcrypt_generic_deinit($td); mcrypt_module_close($td);


Solo recomendaría el cifrado de clave pública si desea establecer la contraseña de un usuario sin su interacción (esto puede ser útil para restablecimientos y contraseñas compartidas).

Llave pública

  1. La extensión OpenSSL , específicamente openssl_public_encrypt y openssl_private_decrypt
  2. Esto sería RSA correcto suponiendo que sus contraseñas encajarán en el tamaño de clave - relleno, de lo contrario, necesita una capa simétrica
  3. Almacene ambas claves para cada usuario, la frase de contraseña de la clave privada es la contraseña de su aplicación

Simétrico

  1. La extensión Mcrypt
  2. AES-256 es probablemente una apuesta segura, pero esta podría ser una pregunta TAN en sí misma
  3. No, esta sería su contraseña de aplicación

Ambos

4 . Sí: los usuarios tendrían que ingresar la contraseña de su aplicación cada vez, pero almacenarla en la sesión plantearía otros problemas

5 .

  • Si alguien roba los datos de la aplicación, es tan seguro como el cifrado simétrico (para el esquema de clave pública, se usa para proteger la clave privada con la frase de contraseña).
  • Su aplicación definitivamente solo debe ser accesible a través de SSL, preferiblemente usando certificados de clientes.
  • Considere agregar un segundo factor de autenticación que solo se usará una vez por sesión, como un token enviado por SMS.

Use password_hash y password_verify

<?php /** * In this case, we want to increase the default cost for BCRYPT to 12. * Note that we also switched to BCRYPT, which will always be 60 characters. */ $options = [ ''cost'' => 12, ]; echo password_hash("rasmuslerdorf", PASSWORD_BCRYPT, $options)."/n"; ?>

Y para descifrar:

<?php // See the password_hash() example to see where this came from. $hash = ''$2y$07$BCryptRequires22Chrcte/VlQH0piJtjXl.0t1XkA8pw9dMXTpOq''; if (password_verify(''rasmuslerdorf'', $hash)) { echo ''Password is valid!''; } else { echo ''Invalid password.''; } ?>


¿Cómo encripto y descifro una contraseña en PHP? Al implementar uno de muchos algoritmos de encriptación. (o usando una de muchas bibliotecas)

¿Cuál es el algoritmo más seguro para cifrar las contraseñas? Hay toneladas de algoritmos diferentes, ninguno de los cuales es 100% seguro. Pero muchos de ellos son lo suficientemente seguros para el comercio e incluso con fines militares

¿Dónde guardo la clave privada? Si ha decidido implementar una clave pública - algoritmo de criptografía (por ejemplo, RSA), no almacena la clave privada. el usuario tiene una clave privada su sistema tiene una clave pública que podría almacenarse en cualquier lugar que desee.

En lugar de almacenar la clave privada, ¿es una buena idea solicitar a los usuarios que ingresen la clave privada cada vez que necesiten una contraseña descifrada? (Se puede confiar en los usuarios de esta aplicación) Bueno, si su usuario puede recordar números primos ridículamente largos, entonces - sí, ¿por qué no? Pero, en general, necesitaría encontrar el sistema que permita al usuario almacenar su clave en algún lugar.

¿De qué manera se puede robar y descifrar la contraseña? ¿De qué debo ser consciente? Esto depende del algoritmo utilizado. Sin embargo, siempre asegúrese de no enviar una contraseña sin cifrar al usuario o desde él. Encripte / descifre en el lado del cliente, o use https (o use otros medios criptográficos para asegurar la conexión entre el servidor y el cliente).

Sin embargo, si todo lo que necesita es almacenar las contraseñas de forma encriptada, le sugiero que use un cifrado XOR simple. El principal problema con este algoritmo es que podría ser fácilmente roto por análisis de frecuencia. Sin embargo, como generalmente las contraseñas no están hechas de largos párrafos de texto en inglés, no creo que deba preocuparse por ello. El segundo problema con XOR Cipher es que si tiene un mensaje en forma cifrada y descifrada, puede encontrar fácilmente la contraseña con la que se cifró. De nuevo, no es un gran problema en su caso, ya que solo afecta al usuario que ya se vio comprometido por otros medios.


Personalmente, usaría mcrypt como otros publicados. Pero hay mucho más para notar ...

  1. ¿Cómo encripto y descifro una contraseña en PHP?

    Vea a continuación una clase sólida que se ocupa de todo por usted:

  2. ¿Cuál es el algoritmo más seguro para cifrar las contraseñas?

    más seguro ? cualquiera de ellos. El método más seguro para encriptar es protegerse contra las vulnerabilidades de divulgación de información (XSS, inclusión remota, etc.). Si se produce, el atacante puede descifrar el cifrado (ningún cifrado es 100% irreversible sin la clave). Como @NullUserException señala que esto no es del todo cierto. Hay algunos esquemas de cifrado que son imposibles de descifrar, como OneTimePad . .

  3. ¿Dónde guardo la clave privada?

    Lo que haría es usar 3 teclas. Uno es proporcionado por el usuario, uno es específico de la aplicación y el otro es específico del usuario (como una sal). La clave específica de la aplicación se puede almacenar en cualquier lugar (en un archivo de configuración fuera de la raíz web, en una variable de entorno, etc.). El usuario específico se almacenaría en una columna en la base de datos junto a la contraseña cifrada. El usuario suministrado no sería almacenado. Entonces, harías algo como esto:

    $key = $userKey . $serverKey . $userSuppliedKey;

    El beneficio allí es que cualquier 2 de las claves puede verse comprometida sin comprometer los datos. Si hay un ataque de inyección SQL, pueden obtener $userKey , pero no los otros 2. Si hay un exploit de servidor local, pueden obtener $userKey y $serverKey , pero no el tercer $userSuppliedKey . Si golpean al usuario con una llave inglesa, pueden obtener $userSuppliedKey , pero no los otros 2 (pero de nuevo, si el usuario es golpeado con una llave inglesa, ya es demasiado tarde).

  4. En lugar de almacenar la clave privada, ¿es una buena idea solicitar a los usuarios que ingresen la clave privada cada vez que necesiten una contraseña descifrada? (Se puede confiar en los usuarios de esta aplicación)

    Absolutamente. De hecho, esa es la única forma en que lo haría. De lo contrario, necesitaría almacenar una versión no encriptada en un formato de almacenamiento duradero (memoria compartida como APC o memcached, o en un archivo de sesión). Eso te expone a compromisos adicionales. Nunca almacene la versión no encriptada de la contraseña en nada que no sea una variable local.

  5. ¿De qué manera se puede robar y descifrar la contraseña? ¿De qué debo ser consciente?

    Cualquier forma de compromiso de sus sistemas les permitirá ver datos encriptados. Si pueden inyectar código o llegar a su sistema de archivos, pueden ver datos descifrados (ya que pueden editar los archivos que descifran los datos). Cualquier forma de ataque de repetición o MITM también les dará acceso completo a las claves involucradas. Detectar el tráfico HTTP sin procesar también les dará las claves.

    Use SSL para todo el tráfico. Y asegúrese de que nada en el servidor tenga ningún tipo de vulnerabilidad (CSRF, XSS, Inyección SQL, Ampliación de privilegios, Ejecución remota de código, etc.).

Editar: Aquí hay una implementación de clase PHP de un método de encriptación fuerte:

/** * A class to handle secure encryption and decryption of arbitrary data * * Note that this is not just straight encryption. It also has a few other * features in it to make the encrypted data far more secure. Note that any * other implementations used to decrypt data will have to do the same exact * operations. * * Security Benefits: * * - Uses Key stretching * - Hides the Initialization Vector * - Does HMAC verification of source data * */ class Encryption { /** * @var string $cipher The mcrypt cipher to use for this instance */ protected $cipher = ''''; /** * @var int $mode The mcrypt cipher mode to use */ protected $mode = ''''; /** * @var int $rounds The number of rounds to feed into PBKDF2 for key generation */ protected $rounds = 100; /** * Constructor! * * @param string $cipher The MCRYPT_* cypher to use for this instance * @param int $mode The MCRYPT_MODE_* mode to use for this instance * @param int $rounds The number of PBKDF2 rounds to do on the key */ public function __construct($cipher, $mode, $rounds = 100) { $this->cipher = $cipher; $this->mode = $mode; $this->rounds = (int) $rounds; } /** * Decrypt the data with the provided key * * @param string $data The encrypted datat to decrypt * @param string $key The key to use for decryption * * @returns string|false The returned string if decryption is successful * false if it is not */ public function decrypt($data, $key) { $salt = substr($data, 0, 128); $enc = substr($data, 128, -64); $mac = substr($data, -64); list ($cipherKey, $macKey, $iv) = $this->getKeys($salt, $key); if (!hash_equals(hash_hmac(''sha512'', $enc, $macKey, true), $mac)) { return false; } $dec = mcrypt_decrypt($this->cipher, $cipherKey, $enc, $this->mode, $iv); $data = $this->unpad($dec); return $data; } /** * Encrypt the supplied data using the supplied key * * @param string $data The data to encrypt * @param string $key The key to encrypt with * * @returns string The encrypted data */ public function encrypt($data, $key) { $salt = mcrypt_create_iv(128, MCRYPT_DEV_URANDOM); list ($cipherKey, $macKey, $iv) = $this->getKeys($salt, $key); $data = $this->pad($data); $enc = mcrypt_encrypt($this->cipher, $cipherKey, $data, $this->mode, $iv); $mac = hash_hmac(''sha512'', $enc, $macKey, true); return $salt . $enc . $mac; } /** * Generates a set of keys given a random salt and a master key * * @param string $salt A random string to change the keys each encryption * @param string $key The supplied key to encrypt with * * @returns array An array of keys (a cipher key, a mac key, and a IV) */ protected function getKeys($salt, $key) { $ivSize = mcrypt_get_iv_size($this->cipher, $this->mode); $keySize = mcrypt_get_key_size($this->cipher, $this->mode); $length = 2 * $keySize + $ivSize; $key = $this->pbkdf2(''sha512'', $key, $salt, $this->rounds, $length); $cipherKey = substr($key, 0, $keySize); $macKey = substr($key, $keySize, $keySize); $iv = substr($key, 2 * $keySize); return array($cipherKey, $macKey, $iv); } /** * Stretch the key using the PBKDF2 algorithm * * @see http://en.wikipedia.org/wiki/PBKDF2 * * @param string $algo The algorithm to use * @param string $key The key to stretch * @param string $salt A random salt * @param int $rounds The number of rounds to derive * @param int $length The length of the output key * * @returns string The derived key. */ protected function pbkdf2($algo, $key, $salt, $rounds, $length) { $size = strlen(hash($algo, '''', true)); $len = ceil($length / $size); $result = ''''; for ($i = 1; $i <= $len; $i++) { $tmp = hash_hmac($algo, $salt . pack(''N'', $i), $key, true); $res = $tmp; for ($j = 1; $j < $rounds; $j++) { $tmp = hash_hmac($algo, $tmp, $key, true); $res ^= $tmp; } $result .= $res; } return substr($result, 0, $length); } protected function pad($data) { $length = mcrypt_get_block_size($this->cipher, $this->mode); $padAmount = $length - strlen($data) % $length; if ($padAmount == 0) { $padAmount = $length; } return $data . str_repeat(chr($padAmount), $padAmount); } protected function unpad($data) { $length = mcrypt_get_block_size($this->cipher, $this->mode); $last = ord($data[strlen($data) - 1]); if ($last > $length) return false; if (substr($data, -1 * $last) !== str_repeat(chr($last), $last)) { return false; } return substr($data, 0, -1 * $last); } }

Tenga en cuenta que estoy usando una función agregada en PHP 5.6: hash_equals . Si tiene menos de 5.6, puede usar esta función sustituta que implementa una función de comparación de tiempo seguro usando doble verificación de HMAC :

function hash_equals($a, $b) { $key = mcrypt_create_iv(128, MCRYPT_DEV_URANDOM); return hash_hmac(''sha512'', $a, $key) === hash_hmac(''sha512'', $b, $key); }

Uso:

$e = new Encryption(MCRYPT_BLOWFISH, MCRYPT_MODE_CBC); $encryptedData = $e->encrypt($data, $key);

Entonces, para descifrar:

$e2 = new Encryption(MCRYPT_BLOWFISH, MCRYPT_MODE_CBC); $data = $e2->decrypt($encryptedData, $key);

Tenga en cuenta que utilicé $e2 la segunda vez para mostrarle que diferentes instancias seguirán descifrando los datos correctamente.

Ahora, ¿cómo funciona? ¿Por qué usarlo sobre otra solución?

  1. Llaves

    • Las claves no se usan directamente. En cambio, la clave se estira mediante una derivación PBKDF2 estándar.

    • La clave utilizada para el cifrado es única para cada bloque de texto encriptado. La clave suministrada se convierte en una "llave maestra". Por lo tanto, esta clase proporciona la rotación de claves para las claves de cifrado y autenticación.

    • NOTA IMPORTANTE , el parámetro $rounds está configurado para claves aleatorias verdaderas con suficiente fuerza (128 bits de seguridad criptográficamente aleatoria como mínimo). Si va a utilizar una contraseña o una clave no aleatoria (o menos aleatorio y luego 128 bits de CS aleatorios), debe aumentar este parámetro. Sugiero un mínimo de 10000 para contraseñas (cuanto más puedas pagar, mejor, pero se agregará al tiempo de ejecución) ...

  2. Integridad de los datos

    • La versión actualizada usa ENCRYPT-THEN-MAC, que es un método mucho mejor para garantizar la autenticidad de los datos encriptados.
  3. Cifrado:

    • Utiliza mcrypt para realizar realmente el cifrado. Sugeriría utilizar MCRYPT_BLOWFISH o MCRYPT_RIJNDAEL_128 y MCRYPT_MODE_CBC para el modo. Es lo suficientemente fuerte y aún bastante rápido (un ciclo de cifrado y descifrado demora aproximadamente 1/2 segundo en mi máquina).

Ahora, en cuanto al punto 3 de la primera lista, lo que eso le daría es una función como esta:

function makeKey($userKey, $serverKey, $userSuppliedKey) { $key = hash_hmac(''sha512'', $userKey, $serverKey); $key = hash_hmac(''sha512'', $key, $userSuppliedKey); return $key; }

Podrías estirarlo en la función makeKey() , pero como se va a estirar más tarde, no hay realmente un gran punto para hacerlo.

En cuanto al tamaño de almacenamiento, depende del texto sin formato. Blowfish utiliza un tamaño de bloque de 8 bytes, por lo que tendrás:

  • 16 bytes para la sal
  • 64 bytes para el hmac
  • longitud de datos
  • Relleno para que la longitud de los datos% 8 == 0

Entonces, para una fuente de datos de 16 caracteres, habrá 16 caracteres de datos para ser encriptados. Entonces eso significa que el tamaño real de los datos cifrados es de 16 bytes debido al relleno. A continuación, agregue los 16 bytes para la sal y 64 bytes para el hmac y el tamaño total almacenado es de 96 bytes. Entonces, en el mejor de los casos hay una sobrecarga de 80 caracteres, y en el peor, una sobrecarga de 87 caracteres ...

Espero que eso ayude...

Nota: 11/12/12: Acabo de actualizar esta clase con un MUCHO mejor método de encriptación, usando mejores claves derivadas y arreglando la generación MAC ...