¿Cómo se usa bcrypt para contraseñas hash en PHP?


1256

De vez en cuando escucho el consejo "Use bcrypt para almacenar contraseñas en PHP, reglas de bcrypt".

Pero que es bcrypt? PHP no ofrece ninguna de esas funciones, Wikipedia balbucea sobre una utilidad de cifrado de archivos y las búsquedas en la Web solo revelan algunas implementaciones de Blowfish en diferentes idiomas. Ahora Blowfish también está disponible en PHP a través de mcrypt, pero ¿cómo ayuda eso a almacenar contraseñas? Blowfish es un cifrado de propósito general, funciona de dos maneras. Si se puede cifrar, se puede descifrar. Las contraseñas necesitan una función de hashing unidireccional.

¿Cuál es la explicación?


13
Esta pregunta se ha abordado anteriormente , y su sugerencia de usar una biblioteca estándar es excelente. La seguridad es un asunto complicado, y al usar un paquete diseñado por alguien que sabe qué demonios están haciendo, solo te estás ayudando a ti mismo.
eykanal

59
@eykanal: esa página ni siquiera menciona bcrypt, y mucho menos explica qué es .
Vilx-

8
@eykanal: no pido una explicación de cómo funciona. Solo quiero saber de qué se trata. Porque lo que sea que pueda desenterrar en la red con la palabra clave "bcrypt", no se puede utilizar de ninguna manera para contraseñas hash. No directamente de todos modos, y no en PHP. OK, por ahora entiendo que es realmente el paquete "phpass" el que utiliza blowfish para encriptar su contraseña con una clave que se deriva de su contraseña (en esencia, encriptar la contraseña consigo mismo). Pero hacer referencia a él como "bcrypt" es muy engañoso, y eso es lo que quería aclarar en esta pregunta.
Vilx-

3
@Vilx: he agregado más información sobre por qué bcryptes un algoritmo de hash unidireccional versus un esquema de cifrado en mi respuesta . Existe todo este concepto erróneo que bcryptes solo Blowfish cuando, de hecho, tiene una programación de teclas totalmente diferente que garantiza que el texto sin formato no se pueda recuperar del texto de cifrado sin conocer el estado inicial del cifrado (sal, rondas, clave).
Andrew Moore

1
Consulte también el marco de hashing de contraseñas PHP portátil de Openwall (PHPass). Está reforzado contra una serie de ataques comunes contra las contraseñas de los usuarios.
jww

Respuestas:


1065

bcryptes un algoritmo de hash que es escalable con hardware (a través de un número configurable de rondas). Su lentitud y múltiples rondas aseguran que un atacante debe implementar fondos y hardware masivos para poder descifrar sus contraseñas. Agregue a eso sales por contraseña ( bcryptREQUIERE sales) y puede estar seguro de que un ataque es prácticamente inviable sin una cantidad ridícula de fondos o hardware.

bcryptusa el algoritmo Eksblowfish para hacer hash de contraseñas. Si bien la fase de cifrado de Eksblowfish y Blowfish es exactamente la misma, la fase de programación clave de Eksblowfish garantiza que cualquier estado posterior dependa tanto de la sal como de la clave (contraseña del usuario), y ningún estado puede calcularse previamente sin el conocimiento de ambos. Debido a esta diferencia clave, bcryptes un algoritmo de hashing unidireccional. No puede recuperar la contraseña de texto plano sin conocer la sal, las rondas y la clave (contraseña). [ Fuente ]

Cómo usar bcrypt:

Usando PHP> = 5.5-DEV

Las funciones de hash de contraseña ahora se han integrado directamente en PHP> = 5.5 . Ahora puede usar password_hash()para crear un bcrypthash de cualquier contraseña:

<?php
// Usage 1:
echo password_hash('rasmuslerdorf', PASSWORD_DEFAULT)."\n";
// $2y$10$xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
// For example:
// $2y$10$.vGA1O9wmRjrwAVXD98HNOgsNpDczlqm3Jq7KnEd1rVAGv3Fykk1a

// Usage 2:
$options = [
  'cost' => 11
];
echo password_hash('rasmuslerdorf', PASSWORD_BCRYPT, $options)."\n";
// $2y$11$6DP.V0nO7YI3iSki4qog6OQI5eiO6Jnjsqg7vdnb.JgGIsxniOn4C

Para verificar una contraseña proporcionada por el usuario contra un hash existente, puede usar la password_verify()siguiente:

<?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.';
}

Usando PHP> = 5.3.7, <5.5-DEV (también RedHat PHP> = 5.3.3)

Hay una biblioteca de compatibilidad en GitHub creada basada en el código fuente de las funciones anteriores escritas originalmente en C, que proporciona la misma funcionalidad. Una vez que se instala la biblioteca de compatibilidad, el uso es el mismo que el anterior (menos la notación de matriz abreviada si todavía está en la rama 5.3.x).

Usando PHP <5.3.7 (DEPRECATED)

Puede usar la crypt()función para generar hash de bcrypt de cadenas de entrada. Esta clase puede generar sales automáticamente y verificar hashes existentes contra una entrada. Si está utilizando una versión de PHP superior o igual a 5.3.7, se recomienda encarecidamente que utilice la función integrada o la biblioteca de compatibilidad . Esta alternativa se proporciona solo para fines históricos.

class Bcrypt{
  private $rounds;

  public function __construct($rounds = 12) {
    if (CRYPT_BLOWFISH != 1) {
      throw new Exception("bcrypt not supported in this installation. See http://php.net/crypt");
    }

    $this->rounds = $rounds;
  }

  public function hash($input){
    $hash = crypt($input, $this->getSalt());

    if (strlen($hash) > 13)
      return $hash;

    return false;
  }

  public function verify($input, $existingHash){
    $hash = crypt($input, $existingHash);

    return $hash === $existingHash;
  }

  private function getSalt(){
    $salt = sprintf('$2a$%02d$', $this->rounds);

    $bytes = $this->getRandomBytes(16);

    $salt .= $this->encodeBytes($bytes);

    return $salt;
  }

  private $randomState;
  private function getRandomBytes($count){
    $bytes = '';

    if (function_exists('openssl_random_pseudo_bytes') &&
        (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN')) { // OpenSSL is slow on Windows
      $bytes = openssl_random_pseudo_bytes($count);
    }

    if ($bytes === '' && is_readable('/dev/urandom') &&
       ($hRand = @fopen('/dev/urandom', 'rb')) !== FALSE) {
      $bytes = fread($hRand, $count);
      fclose($hRand);
    }

    if (strlen($bytes) < $count) {
      $bytes = '';

      if ($this->randomState === null) {
        $this->randomState = microtime();
        if (function_exists('getmypid')) {
          $this->randomState .= getmypid();
        }
      }

      for ($i = 0; $i < $count; $i += 16) {
        $this->randomState = md5(microtime() . $this->randomState);

        if (PHP_VERSION >= '5') {
          $bytes .= md5($this->randomState, true);
        } else {
          $bytes .= pack('H*', md5($this->randomState));
        }
      }

      $bytes = substr($bytes, 0, $count);
    }

    return $bytes;
  }

  private function encodeBytes($input){
    // The following is code from the PHP Password Hashing Framework
    $itoa64 = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';

    $output = '';
    $i = 0;
    do {
      $c1 = ord($input[$i++]);
      $output .= $itoa64[$c1 >> 2];
      $c1 = ($c1 & 0x03) << 4;
      if ($i >= 16) {
        $output .= $itoa64[$c1];
        break;
      }

      $c2 = ord($input[$i++]);
      $c1 |= $c2 >> 4;
      $output .= $itoa64[$c1];
      $c1 = ($c2 & 0x0f) << 2;

      $c2 = ord($input[$i++]);
      $c1 |= $c2 >> 6;
      $output .= $itoa64[$c1];
      $output .= $itoa64[$c2 & 0x3f];
    } while (true);

    return $output;
  }
}

Puedes usar este código así:

$bcrypt = new Bcrypt(15);

$hash = $bcrypt->hash('password');
$isGood = $bcrypt->verify('password', $hash);

Alternativamente, también puede usar el Framework PHP Hashing Portable .


77
@The Wicked Flea: Lamento decepcionarte, pero mt_rand()también se siembra usando la hora actual y la ID del proceso actual. Por favor ver GENERATE_SEED()en/ext/standard/php_rand.h .
Andrew Moore

53
@ Mike: ¡Adelante, está ahí exactamente por esa razón!
Andrew Moore

14
Para cualquiera que piense que necesita modificar el comienzo de la cadena $ salt en la función getSalt, eso no es necesario. El $ 2a $ __ es parte de la sal CRYPT_BLOWFISH. De los documentos: "Blowfish hashing con sal de la siguiente manera:" $ 2a $ ", un parámetro de costo de dos dígitos," $ "y 22 dígitos del alfabeto".
jwinn

18
@MichaelLang: Lo bueno crypt()es revisado por pares y verificado luego. El código anterior llama a PHP crypt(), que llama a la crypt()función POSIX . Todo el código anterior hace más es generar una sal aleatoria (que no tiene que ser criptográficamente segura, la sal no se considera un secreto) antes de llamar crypt(). Tal vez deberías investigar un poco antes de llamar al lobo.
Andrew Moore el

31
Tenga en cuenta que esta respuesta, si bien es buena, comienza a mostrar su antigüedad. Este código (como cualquier implementación de PHP en la que se base crypt()) está sujeto a una vulnerabilidad de seguridad anterior a la 5.3.7 y es (muy levemente) ineficiente posterior a la 5.3.7: aquí se pueden encontrar detalles del problema relevante . Tenga en cuenta también que la nueva API de hashing de contraseña ( compatibilidad con versiones anteriores ) es ahora el método preferido para implementar el hash de contraseña de bcrypt en su aplicación.
DaveRandom

295

Entonces, ¿quieres usar bcrypt? ¡Increíble! Sin embargo, al igual que otras áreas de la criptografía, no debería hacerlo usted mismo. Si necesita preocuparse por algo como administrar claves, almacenar sales o generar números aleatorios, lo está haciendo mal.

La razón es simple: es tan trivialmente fácil fastidiar bcrypt . De hecho, si observa casi todos los códigos de esta página, notará que está violando al menos uno de estos problemas comunes.

Acéptalo, la criptografía es difícil.

Déjalo para los expertos. Déjelo a las personas cuyo trabajo es mantener estas bibliotecas. Si necesita tomar una decisión, lo está haciendo mal.

En cambio, solo usa una biblioteca. Existen varios según sus requisitos.

Bibliotecas

Aquí hay un desglose de algunas de las API más comunes.

PHP 5.5 API - (Disponible para 5.3.7+)

A partir de PHP 5.5, se está introduciendo una nueva API para contraseñas hash. También hay una biblioteca de compatibilidad de shim mantenida (por mí) para 5.3.7+. Esto tiene el beneficio de ser una implementación revisada por pares y fácil de usar.

function register($username, $password) {
    $hash = password_hash($password, PASSWORD_BCRYPT);
    save($username, $hash);
}

function login($username, $password) {
    $hash = loadHashByUsername($username);
    if (password_verify($password, $hash)) {
        //login
    } else {
        // failure
    }
}

Realmente, pretende ser extremadamente simple.

Recursos:

Zend \ Crypt \ Password \ Bcrypt (5.3.2+)

Esta es otra API que es similar a la de PHP 5.5 y tiene un propósito similar.

function register($username, $password) {
    $bcrypt = new Zend\Crypt\Password\Bcrypt();
    $hash = $bcrypt->create($password);
    save($user, $hash);
}

function login($username, $password) {
    $hash = loadHashByUsername($username);
    $bcrypt = new Zend\Crypt\Password\Bcrypt();
    if ($bcrypt->verify($password, $hash)) {
        //login
    } else {
        // failure
    }
}

Recursos:

PasswordLib

Este es un enfoque ligeramente diferente para el hashing de contraseñas. En lugar de simplemente admitir bcrypt, PasswordLib admite una gran cantidad de algoritmos de hash. Es principalmente útil en contextos en los que necesita compatibilidad con sistemas heredados y dispares que pueden estar fuera de su control. Es compatible con una gran cantidad de algoritmos de hash. Y es compatible con 5.3.2+

function register($username, $password) {
    $lib = new PasswordLib\PasswordLib();
    $hash = $lib->createPasswordHash($password, '$2y$', array('cost' => 12));
    save($user, $hash);
}

function login($username, $password) {
    $hash = loadHashByUsername($username);
    $lib = new PasswordLib\PasswordLib();
    if ($lib->verifyPasswordHash($password, $hash)) {
        //login
    } else {
        // failure
    }
}

Referencias

  • Código fuente / documentación: GitHub

PHPASS

Esta es una capa que admite bcrypt, pero también admite un algoritmo bastante fuerte que es útil si no tiene acceso a PHP> = 5.3.2 ... En realidad, admite PHP 3.0+ (aunque no con bcrypt).

function register($username, $password) {
    $phpass = new PasswordHash(12, false);
    $hash = $phpass->HashPassword($password);
    save($user, $hash);
}

function login($username, $password) {
    $hash = loadHashByUsername($username);
    $phpass = new PasswordHash(12, false);
    if ($phpass->CheckPassword($password, $hash)) {
        //login
    } else {
        // failure
    }
}

Recursos

Nota: ¡No use las alternativas PHPASS que no están alojadas en openwall, son proyectos diferentes!

Sobre BCrypt

Si observa, cada una de estas bibliotecas devuelve una sola cadena. Eso se debe a cómo BCrypt funciona internamente. Y hay MUCHAS respuestas sobre eso. Aquí hay una selección que he escrito, que no copiaré / pegaré aquí, pero enlace a:

Envolver

Hay muchas opciones diferentes. Lo que elijas depende de ti. Sin embargo, recomiendo encarecidamente que utilice una de las bibliotecas anteriores para manejar esto por usted.

Nuevamente, si está usando crypt()directamente, probablemente esté haciendo algo mal. Si su código está usando hash()(o md5()o sha1()) directamente, casi definitivamente está haciendo algo mal.

Solo usa una biblioteca ...


77
La sal tiene que ser generada aleatoriamente, sin embargo, no necesita provenir de una fuente aleatoria segura. La sal no es un secreto . Ser capaz de adivinar la próxima sal no tiene un impacto real en la seguridad; siempre que provengan de un conjunto de datos suficientemente grande para generar diferentes sales para cada contraseña codificada, está bien. Recuerde, la sal está ahí para evitar el uso de tablas de arcoíris si sus hashes caen en malas manos. No son secretos
Andrew Moore

77
@AndrewMoore absolutamente correcto! Sin embargo, la sal debe tener suficiente entropía para ser estadísticamente única. No solo en su aplicación, sino en todas las aplicaciones. Por lo tanto, mt_rand()tiene un período lo suficientemente alto, pero el valor inicial es de solo 32 bits. Por lo tanto, el uso mt_rand()efectivamente lo limita a solo 32 bits de entropía. Lo que gracias al problema del cumpleaños significa que tienes un 50% de posibilidades de colisión con solo 7k de sales generadas (globalmente). Como bcryptacepta 128 bits de sal, es mejor usar una fuente que pueda suministrar los 128 bits ;-). (a 128 bits, 50% de probabilidad de colisión ocurre en 2e19 hashes) ...
ircmaxell

1
@ircmaxell: Preste atención al "conjunto de datos suficientemente grande". Sin embargo, su fuente no tiene que ser una fuente de entropía MUY ALTA, solo lo suficientemente alta para los 128 bits. Sin embargo, si ha agotado todas sus fuentes disponibles (no tiene OpenSSL, etc.) y su único recurso es mt_rand (), aún es mejor que la alternativa (que es rand ()).
Andrew Moore

44
@ AndrewMoore: absolutamente. No discutiendo eso. Solo eso mt_randy uniqid(y por lo tanto lcg_valuey rand) no son las primeras opciones ...
ircmaxell

1
ircmaxell, muchas gracias por la biblioteca password_compat para 5.3.xx, no la hemos necesitado antes, pero ahora sí, en un servidor php 5.3.xx, y gracias por sus consejos claros para no intentar hacer esta lógica. uno mismo.
Lizardx

47

Obtendrá mucha información en Enough With The Rainbow Tables: lo que necesita saber sobre esquemas de contraseña segura o marco de hash de contraseña PHP portátil .

El objetivo es descifrar la contraseña con algo lento, por lo que alguien que obtenga su base de datos de contraseñas morirá tratando de forzarla (una demora de 10 ms para verificar una contraseña no es nada para usted, mucho para alguien que intenta forzarla). Bcrypt es lento y puede usarse con un parámetro para elegir qué tan lento es.


77
Haga cumplir lo que quiera, los usuarios lograrán fastidiar y usar la misma contraseña en varias cosas. Por lo tanto, debe protegerlo lo más posible o implementar algo que le permita no tener que almacenar ninguna contraseña (SSO, openID, etc.).
Arkh

41
No. El hashing de contraseñas se usa para proteger contra un ataque: alguien robó su base de datos y quiere obtener un inicio de sesión de texto en claro + contraseñas.
Arkh

44
@Josh K. Le animo a que intente descifrar algunas contraseñas simples después de ajustarlas a través de phpass, por lo que toma entre 1 ms y 10 ms calcularlas en su servidor web.
Arkh

3
Convenido. Pero el tipo de usuario que usará qwerty como contraseña también es el tipo de usuario que marcará cualquier complicada en algún lugar donde él (y los atacantes) puedan leerla fácilmente. Lo que logra el uso de bcrypt es que cuando su base de datos se haga pública en contra de su voluntad, será más difícil llegar a aquellos usuarios que tengan alguna contraseña como ^ | $$ & ZL6- £ que si usara sha512 de una sola vez.
Arkh

44
@coreyward vale la pena señalar que hacer eso es más dañino que no bloquear en absoluto; eso se considera fácilmente un vector de "denegación de servicio". Simplemente comience a enviar correos no deseados en cualquier cuenta conocida y puede interrumpir a muchos usuarios muy, muy fácilmente. Es mejor atrapar (retrasar) al atacante que negarle el acceso, especialmente si es un cliente que paga.
damianb

36

Puede crear un hash unidireccional con bcrypt utilizando la crypt()función de PHP y pasando una sal de Blowfish adecuada. Lo más importante de toda la ecuación es que A) el algoritmo no se ha visto comprometido y B) se salpica correctamente cada contraseña . No use una sal para toda la aplicación; que abre toda su aplicación para atacar desde un solo conjunto de tablas Rainbow.

PHP - Función de cripta


44
Este es el enfoque correcto: use la crypt()función de PHP , que admite varias funciones de hash de contraseña diferentes. Asegúrese de no estar utilizando CRYPT_STD_DESo CRYPT_EXT_DES- cualquiera de los otros tipos compatibles está bien (e incluye bcrypt, bajo el nombre CRYPT_BLOWFISH).
caf

44
SHA también tiene un parámetro de costo, a través de la opción 'rondas'. Cuando uso eso, tampoco veo ninguna razón para favorecer bcrypt.
Pieter Ennes

3
En realidad, un solo SHA-1 (o MD5) de una contraseña sigue siendo fácilmente resistente a la fuerza bruta, con o sin sal (la sal ayuda contra las tablas del arco iris, no contra la fuerza bruta). Usa bcrypt.
Paŭlo Ebermann

Me resulta inquietante que todo el mundo parezca decir "bcrypt" cuando se refieren a la cripta de php ().
Sliq

3
@Panique ¿Por qué? El algoritmo se llama bcrypt . cryptexpone varios hashes de contraseña, con bcrypt correspondiente a la CRYPT_BLOWFISHconstante. Bcrypt es actualmente el algoritmo más fuerte soportado por crypty muchos otros que soporta son bastante débiles.
CodesInChaos

34

Editar: 2013.01.15 - Si su servidor lo admitirá, use la solución de martinstoeckli en su lugar.


Todos quieren hacer esto más complicado de lo que es. La función crypt () hace la mayor parte del trabajo.

function blowfishCrypt($password,$cost)
{
    $chars='./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    $salt=sprintf('$2y$%02d$',$cost);
//For PHP < PHP 5.3.7 use this instead
//    $salt=sprintf('$2a$%02d$',$cost);
    //Create a 22 character salt -edit- 2013.01.15 - replaced rand with mt_rand
    mt_srand();
    for($i=0;$i<22;$i++) $salt.=$chars[mt_rand(0,63)];
    return crypt($password,$salt);
}

Ejemplo:

$hash=blowfishCrypt('password',10); //This creates the hash
$hash=blowfishCrypt('password',12); //This creates a more secure hash
if(crypt('password',$hash)==$hash){ /*ok*/ } //This checks a password

Sé que debería ser obvio, pero no uses 'contraseña' como contraseña.


3
La creación de la sal podría mejorarse (use la fuente aleatoria del sistema operativo), de lo contrario, me parece bien. Para las nuevas versiones de PHP, es mejor usar en 2ylugar de 2a.
martinstoeckli

utilizar mcrypt_create_iv($size, MCRYPT_DEV_URANDOM)como fuente de sal.
CodesInChaos

Echaré un vistazo más de cerca a mcrypt_create_iv () cuando tenga un momento, si nada más debería mejorar ligeramente el rendimiento.
Jon Hulka

2
Agregue codificación Base64 y traduzca a los bcryptusos personalizados del alfabeto . mcrypt_create_iv(17, MCRYPT_DEV_URANDOM), str_replace('+', '.', base64_encode($rawSalt)),$salt = substr($salt, 0, 22);
CodesInChaos

1
@ JonHulka - Eche un vistazo al paquete de compatibilidad de PHP [Línea 127], esta es una implementación sencilla.
martinstoeckli

29

La versión 5.5 de PHP tendrá soporte incorporado para BCrypt, las funciones password_hash()y password_verify(). En realidad, estos son solo envoltorios alrededor de la función crypt(), y facilitarán su uso correcto. Se encarga de la generación de una sal aleatoria segura y proporciona buenos valores predeterminados.

La forma más fácil de usar estas funciones será:

$hashToStoreInDb = password_hash($password, PASSWORD_BCRYPT);
$isPasswordCorrect = password_verify($password, $existingHashFromDb);

Este código codificará la contraseña con BCrypt (algoritmo 2y), genera una sal aleatoria de la fuente aleatoria del sistema operativo y utiliza el parámetro de costo predeterminado (en este momento es 10). La segunda línea verifica si el usuario ingresó la contraseña que coincide con un valor hash ya almacenado.

Si desea cambiar el parámetro de costo, puede hacerlo así, aumentando el parámetro de costo en 1, duplicando el tiempo necesario para calcular el valor hash:

$hash = password_hash($password, PASSWORD_BCRYPT, array("cost" => 11));

A diferencia del "cost"parámetro, es mejor omitir"salt" parámetro, porque la función ya hace todo lo posible para crear una sal criptográficamente segura.

Para PHP versión 5.3.7 y posterior, existe un paquete de compatibilidad , del mismo autor que realizó la password_hash()función. Para las versiones de PHP 5.3.7 antes no hay apoyo para crypt()con 2yel algoritmo Bcrypt segura Unicode. En su lugar 2a, se podría reemplazar con , que es la mejor alternativa para versiones anteriores de PHP.


3
Después de leer esto, mi primer pensamiento fue "¿cómo se almacena la sal que se genera"? Después de examinar los documentos, la función password_hash () termina generando una cadena que almacena el método de cifrado, la sal y el hash generado. Por lo tanto, solo almacena todo lo que necesita en una cadena para que funcione la función password_verify (). Solo quería mencionar esto, ya que puede ayudar a otros cuando lo ven.
jzimmerman2011

@ jzimmerman2011 - Exactamente, en otra respuesta intenté explicar este formato de almacenamiento con un ejemplo.
martinstoeckli

7

Pensamiento actual: los hash deben ser los más lentos disponibles, no los más rápidos posibles. Esto suprime la mesa arcoiris ataques de la .

También relacionado, pero con precaución: un atacante nunca debe tener acceso ilimitado a su pantalla de inicio de sesión. Para evitar eso: configure una tabla de seguimiento de direcciones IP que registre cada golpe junto con el URI. Si más de 5 intentos de inicio de sesión provienen de la misma dirección IP en un período de cinco minutos, bloquee con una explicación. Un enfoque secundario es tener un esquema de contraseña de dos niveles, como lo hacen los bancos. Poner un bloqueo por fallas en el segundo pase aumenta la seguridad.

Resumen: ralentice al atacante mediante el uso de funciones hash que consumen mucho tiempo. Además, bloquee demasiados accesos a su inicio de sesión y agregue un segundo nivel de contraseña.


Creo que suponen que el atacante ya ha logrado robar mi base de datos por algún otro medio, y ahora está tratando de sacar las contraseñas para probarlas en PayPal o algo así.
Vilx-

44
A mediados de 2012 y esta respuesta todavía es inestable, ¿cómo un algoritmo de hash lento previene los ataques de la mesa arcoiris? Pensé que una sal de rango de bytes aleatorio hizo? Siempre pensé que la velocidad del algoritmo de hash determina cuántas iteraciones pueden enviar contra el hash que obtuvieron en un período de tiempo específico. Además, NUNCA BLOQUEE A UN USUARIO EN INTENTOS DE INICIACIÓN DE SESIÓN FALLIDOS, confíe en que sus usuarios se cansarán, a menudo en algunos sitios necesito iniciar sesión cerca de 5 veces, a veces más antes de recordar mi contraseña. Además, el segundo nivel de paso no funciona, aunque la autenticación de dos pasos con el código del teléfono móvil podría funcionar.
Sammaye

1
@Sammaye, estaría de acuerdo con esto hasta cierto punto. Configuré un bloqueo en 5 intentos fallidos de inicio de sesión, antes de subirlo rápidamente a 7, luego 10 ahora está en 20. Ningún usuario normal debería tener 20 intentos fallidos de inicio de sesión, pero es lo suficientemente bajo como para detener fácilmente los ataques de fuerza bruta
Bruce Aldridge

@BruceAldridge Personalmente, creo que sería mejor hacer que su script se detenga por un tiempo aleatorio después de decir, 7 inicios de sesión fallidos y mostrar un captcha en lugar de bloquear. El bloqueo es un movimiento muy agresivo.
Sammaye

1
@Sammaye Estoy de acuerdo en que los bloques permanentes son malos. Me refiero a un bloqueo temporal que aumenta con el número de intentos fallidos.
Bruce Aldridge

7

¡Aquí hay una respuesta actualizada a esta vieja pregunta!

La forma correcta de hash contraseñas en PHP desde 5.5 es con password_hash(), y la forma correcta de verificarlas es con password_verify(), y esto sigue siendo cierto en PHP 8.0. Estas funciones usan hash de bcrypt por defecto, pero se han agregado otros algoritmos más fuertes. Puede modificar el factor de trabajo (efectivamente, qué tan "fuerte" es el cifrado) a través depassword_hash parámetros.

Sin embargo, aunque todavía es lo suficientemente fuerte, bcrypt ya no se considera de vanguardia ; Ha llegado un mejor conjunto de algoritmos de hash de contraseña llamado Argon2 , con variantes Argon2i, Argon2d y Argon2id. La diferencia entre ellos (como se describe aquí ):

Argon2 tiene una variante primaria: Argon2id, y dos variantes suplementarias: Argon2d y Argon2i. Argon2d utiliza acceso a memoria dependiente de datos, lo que lo hace adecuado para criptomonedas y aplicaciones de prueba de trabajo sin amenazas de ataques de temporización de canal lateral. Argon2i utiliza acceso a la memoria independiente de los datos, que es el preferido para el hashing de contraseñas y la derivación de claves basadas en contraseñas. Argon2id funciona como Argon2i durante la primera mitad de la primera iteración sobre la memoria, y como Argon2d para el resto, proporcionando así protección de ataque de canal lateral y ahorro de costos por fuerza bruta debido a compensaciones de memoria de tiempo.

El soporte de Argon2i se agregó en PHP 7.2, y lo solicita así:

$hash = password_hash('mypassword', PASSWORD_ARGON2I);

y el soporte Argon2id fue agregado en PHP 7.3:

$hash = password_hash('mypassword', PASSWORD_ARGON2ID);

No se requieren cambios para verificar las contraseñas ya que la cadena hash resultante contiene información sobre qué algoritmo, sal y factores de trabajo se usaron cuando se creó.

Por separado (y de forma algo redundante), libsodium (agregado en PHP 7.2) también proporciona el hash Argon2 a través de las funciones sodium_crypto_pwhash_str ()y sodium_crypto_pwhash_str_verify(), que funcionan de la misma manera que las funciones integradas de PHP. Una posible razón para usarlos es que a veces PHP puede compilarse sin libargon2, lo que hace que los algoritmos Argon2 no estén disponibles para la función password_hash; PHP 7.2 y versiones superiores siempre deben tener libsodium habilitado, pero puede que no sea así, pero al menos hay dos formas de obtener ese algoritmo. Así es como puede crear un hash Argon2id con libsodium (incluso en PHP 7.2, que de lo contrario carece de soporte Argon2id):

$hash = sodium_crypto_pwhash_str(
    'mypassword',
    SODIUM_CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE,
    SODIUM_CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE
);

Tenga en cuenta que no le permite especificar una sal manualmente; Esto es parte del espíritu de libsodium: no permita que los usuarios establezcan parámetros a valores que puedan comprometer la seguridad ; por ejemplo, no hay nada que le impida pasar una cadena de sal vacía a la password_hashfunción de PHP ; ¡libsodium no te deja hacer nada tan tonto!


4

Para las contraseñas de OAuth 2 :

$bcrypt = new \Zend\Crypt\Password\Bcrypt;
$bcrypt->create("youpasswordhere", 10)

1

Como todos sabemos, el almacenamiento de la contraseña en texto claro en la base de datos no es seguro. El bcrypt es una técnica de contraseña hash. Se utiliza para construir la seguridad de la contraseña. Una de las funciones sorprendentes de bcrypt es que nos salva de los piratas informáticos. Se utiliza para proteger la contraseña de ataques de piratería porque la contraseña se almacena en forma cifrada.

la función password_hash () se usa para crear un nuevo hash de contraseña. Utiliza un algoritmo de hashing fuerte y robusto. La función password_hash () es muy compatible con la función crypt (). Por lo tanto, los hashes de contraseña creados por crypt () se pueden usar con password_hash () y viceversa. Las funciones password_verify () y password_hash () son solo los contenedores alrededor de la función crypt (), y hacen que sea mucho más fácil usarlo con precisión.

SINTAXIS

string password_hash($password , $algo , $options)

Los siguientes algoritmos son compatibles actualmente con la función password_hash ():

PASSWORD_DEFAULT PASSWORD_BCRYPT PASSWORD_ARGON2I PASSWORD_ARGON2ID

Parámetros: esta función acepta tres parámetros como se mencionó anteriormente y se describe a continuación:

contraseña : almacena la contraseña del usuario. algo : es la constante del algoritmo de contraseña que se utiliza continuamente mientras se denota el algoritmo que se utilizará cuando se produce el hash de contraseña. opciones : es una matriz asociativa, que contiene las opciones. Si esto se elimina y no incluye, se utilizará una sal aleatoria y se utilizará un costo predeterminado. Valor de retorno : devuelve la contraseña hash en caso de éxito o False en caso de error.

Ejemplo :

Input : echo password_hash("GFG@123", PASSWORD_DEFAULT); Output : $2y$10$.vGA19Jh8YrwSJFDodbfoHJIOFH)DfhuofGv3Fykk1a

Los programas a continuación ilustran la función password_hash () en PHP:

<?php echo password_hash("GFG@123", PASSWORD_DEFAULT); ?>

SALIDA

$2y$10$Z166W1fBdsLcXPVQVfPw/uRq1ueWMA6sLt9bmdUFz9AmOGLdM393G

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.