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


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

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

¿Cuál es la explicación?


1145
2018-01-25 15:34


origen


Respuestas:


bcrypt es un algoritmo 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 desplegar fondos masivos y hardware para poder descifrar sus contraseñas. Agregar a eso por contraseña sales (bcrypt REQUIERE sales) y puede estar seguro de que un ataque es prácticamente inviable sin una cantidad ridícula de fondos o hardware.

bcrypt usa el Eksblowfish algoritmo para hash contraseñas. Mientras que la fase de encriptación de Eksblowfish y Blowfish son exactamente iguales, la fase de programación clave de Eksblowfish asegura que cualquier estado posterior depende tanto de la sal como de la clave (contraseña del usuario), y ningún estado se puede calcular previamente sin el conocimiento de ambos. Debido a esta diferencia clave, bcrypt es un algoritmo hash unidireccional. No puede recuperar la contraseña de texto sin conocer la sal, rondas y clave (contraseña). [Fuente]

Cómo usar bcrypt:

Usando PHP> = 5.5-DEV

Funciones de hashing de contraseña ahora se han construido directamente en PHP> = 5.5. Ahora puede usar password_hash() para crear un bcrypt hash 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 password_verify() como tal:

<?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 un biblioteca de compatibilidad en GitHub creado en base al 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 abreviada de matriz si todavía está en la rama 5.3.x).

Usando PHP <5.3.7 (OBSOLETO)

Puedes usar crypt() función para generar hash bcrypt de cadenas de entrada. Esta clase puede generar automáticamente sales 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 incorporada o la biblioteca compatible.. Esta alternativa se proporciona solo con 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 Portable Hashing Framework PHP.


973
2018-06-12 19:23



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 cosas como la administración de claves, el almacenamiento de sales o la generación de números aleatorios, lo está haciendo mal.

La razón es simple: es tan trivialmente fácil de arruinar la cripta. De hecho, si observa casi cada pieza de código en esta página, notará que está violando al menos uno de estos problemas comunes.

Enfréntalo, la criptografía es difícil.

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

En cambio, solo usa una biblioteca. Varios existen dependiendo de 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 calces mantenida (por mí) para 5.3.7+. Esto tiene la ventaja de ser revisado por pares y sencillo para usar la implementación.

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, está destinado a 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 al hash de contraseñas. En lugar de simplemente respaldar bcrypt, PasswordLib es compatible con una gran cantidad de algoritmos hash. Es principalmente útil en contextos en los que necesita compatibilidad compatible con sistemas legacy y dispares que pueden estar fuera de su control. Es compatible con una gran cantidad de algoritmos 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 sólido que es útil si no tienes acceso a PHP> = 5.3.2 ... En realidad es compatible con 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 de PHPASS que no están alojadas en Openwall, ¡son proyectos diferentes!

Acerca de BCrypt

Si nota, cada una de estas bibliotecas devuelve una sola cadena. Eso se debe a la forma en que BCrypt funciona internamente. Y hay un montón de respuestas sobre eso. Aquí hay una selección que he escrito, que no copiaré / pegaré aquí, sino un enlace a:

Envolver

Hay muchas opciones diferentes. Cuál eliges depende de ti. Sin embargo, lo haría ALTAMENTE recomendamos que use una de las bibliotecas anteriores para manejar esto por usted.

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

Solo usa una biblioteca ...


273
2018-01-25 15:46



Obtendrá mucha información en Suficiente con The Rainbow Tables: lo que debe saber sobre los esquemas de contraseña segura o Marco hash de contraseñas PHP portátil.

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


43
2018-01-25 15:48



Puede crear un hash unidireccional con bcrypt usando PHP crypt() funcionar y pasar una sal de Blowfish apropiada. La más importante de toda la ecuación es que A) el algoritmo no se ha visto comprometido y B) Sacas apropiadamente cada contraseña. No use una sal para toda la aplicación; eso abre toda tu aplicación para atacar desde un solo conjunto de tablas Rainbow.

PHP - Función de criptación


34
2017-10-31 08:25




Edición: 2013.01.15 - Si su servidor lo admite, use La solución de martinstoeckli en lugar.


Todos quieren que esto sea 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 use "contraseña" como contraseña.


32
2018-01-11 08:07



La versión 5.5 de PHP tendrá soporte integrado para BCrypt, las funciones password_hash() y password_verify(). En realidad, estos son solo envoltorios alrededor de la función crypt()y facilitará su uso correcto. Se ocupa 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 cifrará la contraseña con BCrypt (algoritmo 2y), genera una sal aleatoria a partir 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 la contraseña ingresada por el usuario coincide con un valor hash almacenado.

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

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

En contraste con el "cost" parámetro, es mejor omitir el "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 una paquete de compatibilidad, del mismo autor que hizo el password_hash() función. Para las versiones de PHP anteriores a 5.3.7, no hay soporte para crypt() con 2y, el algoritmo BCrypt seguro Unicode. Uno podría reemplazarlo en cambio con 2a, que es la mejor alternativa para versiones anteriores de PHP.


24
2018-02-19 14:17



Una alternativa es usar scrypt, diseñado específicamente para ser superior a bcrypt por Colin Percival en su papel. Hay un scrypt extensión de PHP en PECL. Lo ideal es que este algoritmo se enrolle en PHP para que pueda especificarse para las funciones de password_ * (idealmente como "PASSWORD_SCRYPT"), pero eso aún no está allí.


5
2017-12-07 20:56