172 votos

De dos vías de cifrado: necesito almacenar las contraseñas que se pueden recuperar

Estoy creando una aplicación que se encargará de almacenar las contraseñas que el usuario puede recuperar y ver. Las contraseñas son para un dispositivo de hardware, por lo que comprobar en contra de hashes están fuera de la cuestión.

Lo que necesito saber es:

  1. ¿Cómo cifrar y descifrar una contraseña en PHP?

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

  3. ¿Dónde puedo almacenar la clave privada?

  4. En lugar de almacenar la clave privada, es una buena idea para obligar a los usuarios a introducir la clave privada en cualquier momento que necesiten una contraseña de descifrado? (Los usuarios de esta aplicación pueden ser de confianza)

  5. ¿De qué manera puede la contraseña ser robado y descifrado? ¿Qué debo tener en cuenta?

210voto

ircmaxell Puntos 74865

Personalmente, me gustaría utilizar mcrypt igual que otros publicados. Pero hay mucho más en la nota...

  1. ¿Cómo cifrar y descifrar una contraseña en PHP?

    Vea a continuación una fuerte clase que se encarga de todo para usted:

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

    más seguro? cualquiera de ellos. El método más seguro que si usted va a cifrar es proteger contra la divulgación de información vulnerabilidades de XSS (remoto, de inclusión, etc.). Si lo consigue, el atacante puede eventualmente romper la encriptación (cifrado es el 100% de la onu-reversible sin la clave - Como @NullUserException señala esto no es totalmente cierto. Hay algunos esquemas de cifrado que son imposibles de romper, tales como OneTimePad).

  3. ¿Dónde puedo almacenar la clave privada?

    Lo que me gustaría hacer es usar las teclas 3. Uno es suministrado por el usuario, una es específica de la aplicación y la otra es específica para el usuario (como la sal). La aplicación específica de la clave puede ser almacenado en cualquier lugar (en un archivo de configuración de fuera de la web de la raíz, en una variable de entorno, etc.). El usuario específica podría ser almacenados en una columna en la base de datos junto a la contraseña cifrada. El usuario suministrada no se almacenará. A continuación, te gustaría hacer algo como esto:

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

    El beneficio, es que 2 de las claves puede estar en peligro, sin que los datos sean comprometidos. Si hay un ataque de Inyección de SQL, se puede obtener la $userKey, pero no los otros 2. Si hay un servidor local explotar, pueden ponerse $userKey y $serverKey, pero no el tercero $userSuppliedKey. Si se van a batir el usuario con una llave, se puede obtener la $userSuppliedKey, pero no los otros 2 (pero, de nuevo, si el usuario es golpeado con una llave, que es demasiado tarde de todos modos).

  4. En lugar de almacenar la clave privada, es una buena idea para obligar a los usuarios a introducir la clave privada en cualquier momento que necesiten una contraseña de descifrado? (Los usuarios de esta aplicación pueden ser de confianza)

    Absolutamente. De hecho, esa es la única manera en que yo lo haría. De lo contrario se necesitaría para almacenar sin cifrar versión en una duradera formato de almacenamiento (memoria compartida, tales como APC o memcached, o en un archivo de sesión). Eso es exponerse a compromisos adicionales. Nunca almacene el sin cifrar versión de la contraseña en cualquier cosa excepto una variable local.

  5. ¿De qué manera puede la contraseña ser robado y descifrado? ¿Qué debo tener en cuenta?

    Cualquier forma de compromiso de sus sistemas les permitirá ver los datos cifrados. Si se puede inyectar código o llegar a su sistema de ficheros, se pueden ver los datos descifrados (ya que se puede editar los archivos que descifrar los datos). Cualquier forma de Reproducción o el ataque MITM también les dará pleno acceso a las claves implicadas. Olfateando el raw HTTP tráfico también les damos las claves.

    Utilizar SSL para todo el tráfico. Y asegúrese de que nada en el servidor tiene ningún tipo de vulnerabilidades (CSRF, XSS, Inyección SQL, Escalada de Privilegios, la Ejecución Remota de Código, etc.).

Edit: Aquí está una clase de PHP de la aplicación de un método sólido de cifrado:

/**
 * 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 ($mac !== hash_hmac('sha512', $enc, $macKey, true)) {
             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);
    }
}

Uso:

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

Luego, a descifrar:

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

Tenga en cuenta que he utilizado $e2 en el segundo tiempo para mostrar diferentes instancias se siguen correctamente descifrar los datos.

Ahora, ¿cómo funciona/¿por qué utilizar a lo largo de otra solución:

  1. Las claves

    • Las teclas no son utilizados directamente. En su lugar, la clave está extendido por un estándar PBKDF2 derivación.

    • La clave utilizada para el cifrado es único para cada bloque encriptados de texto. La llave de serie por lo tanto se convierte en una "llave maestra". Esta clase proporciona, pues, la rotación de claves de cifrado y autenticación de claves.

    • NOTA IMPORTANTE, la $rounds parámetro está configurado por cierto teclas al azar de la suficiente fuerza (128 bits de Criptográficamente Seguro azar en un mínimo). Si usted va a utilizar una contraseña, o no clave aleatoria (o menos al azar, a continuación, 128 bits de CS al azar), se debe aumentar este parámetro. Me gustaría sugerir un mínimo de 10000 contraseñas (cuanto más que usted puede permitirse el lujo, el mejor, pero va a añadir al tiempo de ejecución)...

  2. La Integridad De Los Datos

    • La versión actualizada de los usos CIFRAR-ENTONCES-MAC, que es un mejor método para asegurar la autenticidad de los datos cifrados.
  3. Cifrado:

    • Utiliza mcrypt para realmente realizar el cifrado. Me gustaría sugerir el uso de cualquiera MCRYPT_BLOWFISH o MCRYPT_RIJNDAEL_128 cifradores y MCRYPT_MODE_CBC para la modalidad. Es lo suficientemente fuerte, y todavía bastante rápido (de cifrado y descifrado ciclo dura aproximadamente 1/2 segundo en mi máquina).

Ahora, como para el punto 3 de la primera lista, lo que 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;
}

Usted puede estirarlo en el makeKey() función, pero ya que va a ser estirado más tarde, realmente no hay un gran punto de hacerlo.

Tan lejos como el tamaño de almacenamiento, depende del formato de texto. Blowfish utiliza un byte de 8 tamaño de bloque, de manera que dispondrás de:

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

Así, por una de 16 caracteres de origen de datos, no serán de 16 caracteres de datos cifrados. Lo que significa que el real, cifrado de datos de tamaño es de 16 bytes debido a relleno. A continuación, agregar el 16 bytes para la sal y 64 bytes para el hmac y el total almacenado tamaño es de 96 bytes. Así que hay al mejor de un 80 caracteres generales, y en el peor, un 87 carácter sobrecarga...

Espero que le ayuda a...

Nota: 12/11/12: acabo de actualizar esta clase con una mejor método de cifrado, gracias a una mejor derivados de claves, y corrección de la MAC de la generación...

14voto

Ivan Puntos 2346

¿Cómo cifrar y descifrar una contraseña en PHP? Mediante la implementación de uno de los muchos algoritmos de cifrado. (o utilizar una de las muchas bibliotecas)

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

¿Dónde puedo almacenar la clave privada? Si usted ha decidido implementar de clave pública - algoritmo de cifrado(por ejemplo, RSA), que no almacena la clave privada. el usuario tiene la clave privada. el sistema dispone de la clave pública que puede ser almacenada en cualquier lugar que desee.

En lugar de almacenar la clave privada, es una buena idea para obligar a los usuarios a introducir la clave privada en cualquier momento que necesiten una contraseña de descifrado? (Los usuarios de esta aplicación pueden ser de confianza) Bueno, si el usuario pueda recordar ridículamente largas de números primos, a continuación, - sí, por qué no. Pero en general, usted tendría que venir con el sistema que va a permitir a los usuarios almacenar sus claves en algún lugar.

¿De qué manera puede la contraseña ser robado y descifrado? ¿Qué debo tener en cuenta? Esto depende del algoritmo utilizado. Sin embargo, asegúrese siempre de que usted no enviar la contraseña sin cifrar o de los usuarios. Cifrar/descifrar en el lado del cliente, o el uso de https(o de usuario de otros medios criptográficos para garantizar la conexión entre el servidor y el cliente).

Sin embargo, si todo lo que usted necesita almacenar contraseñas en la manera cifrada, me permito sugerir que usted use un simple XOR de Cifrado. El principal problema con este algoritmo es que podría ser fácilmente roto por el análisis de la frecuencia. Sin embargo, como en general las contraseñas no están hechas de largos párrafos de texto en inglés creo que no debe preocuparse por ello. El segundo problema con el Cifrado XOR es que si usted tiene un mensaje en ambos cifrado y descifrado de forma que usted puede encontrar fácilmente la contraseña con la que se ha cifrado. De nuevo, no es un gran problema en tu caso, ya que sólo afecta al usuario que ya se ha visto comprometida por otros medios.

12voto

Jon Rhoades Puntos 313
  1. La función PHP que está después es Mcrypt (http://www.php.net/manual/en/intro.mcrypt.php).

El ejemplo del manual es 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";
?>

Usted podría utilizar mcrypt_decrypt para descifrar tu contraseña.

  1. El mejor algoritmo es bastante subjetivo - pedir 5 personas, recibe 5 respuestas. Personalmente, si el valor predeterminado (Blowfish) no es lo suficientemente bueno para usted, usted probablemente tiene problemas más grandes!

  2. Dado que es necesario por PHP para cifrar - no estoy seguro que se puede ocultar en cualquier lugar - bienvenida a los comentarios sobre esto. Estándar de PHP mejores prácticas de codificación aplicar por supuesto!

  3. Dado que la clave de cifrado será en el código de todos modos, no está seguro de lo que va a ganar, proporcionando el resto de la aplicación es segura.

  4. Obviamente, si el cifrado de la contraseña y la clave de cifrado son robados, entonces game over.

Me gustaría poner un jinete sobre mi respuesta - yo no soy un PHP crypto experto, pero creo que lo que me han contestado es una práctica estándar - doy la bienvenida a comentarios que otros puedan tener.

6voto

Bradley Puntos 651

Una gran cantidad de usuarios han sugerido el uso de mcrypt... lo cual es correcto, pero me gustaría ir un paso más allá para hacer que sea fácil de almacenar y transferir (como a veces los valores cifrados puede hacer mucho para enviar a través de otras tecnologías como la curvatura, o json).

Después de que se haya encriptado usando mcrypt, se ejecuta a través de base64_encode y, a continuación, convertirlo a código hexadecimal. Una vez en código hexadecimal es fácil de transferencia en una variedad de 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);

5voto

Long Ears Puntos 3081

Yo sólo sugieren la clave pública de cifrado si desea que la capacidad para establecer la contraseña de un usuario sin su interacción (esto puede ser útil para restablece y compartido de contraseñas).

Clave pública

  1. El OpenSSL extensión, específicamente openssl_public_encrypt y openssl_private_decrypt
  2. Esto sería hacia RSA asumiendo sus contraseñas caben en el tamaño de la clave de relleno, de lo contrario, usted necesita un simétrica de la capa de
  3. Almacenar claves para cada usuario, la clave privada de la frase de contraseña es su aplicación contraseña

Simétrica

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

Tanto

4. Sí - los usuarios tienen que introducir su contraseña de aplicación en cada momento, pero el almacenamiento en la sesión se plantean otras cuestiones

5.

  • Si alguien roba los datos de la aplicación, es tan seguro como el algoritmo de cifrado simétrico (para la clave pública del esquema, se utiliza para proteger la clave privada con la frase de paso.)
  • Su aplicación debería ser sólo accesible a través de SSL, preferentemente, a través de certificados de cliente.
  • Considerar la adición de un segundo factor de autenticación que iba a ser utilizado sólo una vez por sesión, como un token enviado a través de SMS.

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X