Question Comment utilisez-vous bcrypt pour hacher les mots de passe en PHP?


De temps en temps, j'entends le conseil "Utiliser bcrypt pour stocker les mots de passe en PHP, les règles de bcrypt".

Mais comment ça bcrypt? PHP n'offre pas de telles fonctions, Wikipedia babille sur un utilitaire de chiffrement de fichier et les recherches sur le Web révèlent juste quelques implémentations de Blowfish dans différentes langues. Maintenant, Blowfish est également disponible en PHP via mcrypt, mais comment cela aide-t-il à stocker les mots de passe? Blowfish est un chiffre universel, il fonctionne de deux façons. S'il peut être chiffré, il peut être déchiffré. Les mots de passe nécessitent une fonction de hachage unidirectionnelle.

Quelle est l'explication?


1145
2018-01-25 15:34


origine


Réponses:


bcrypt est un algorithme de hachage qui est évolutif avec le matériel (via un nombre configurable de tours). Sa lenteur et ses tours multiples font qu'un attaquant doit déployer des fonds et du matériel massifs pour pouvoir déchiffrer vos mots de passe. Ajouter à ce per-mot de passe sels (bcrypt EXIGE des sels) et vous pouvez être sûr qu'une attaque est pratiquement irréalisable sans aucune quantité ridicule de fonds ou de matériel.

bcrypt utilise le Eksblowfish algorithme pour hacher les mots de passe. Alors que la phase de cryptage de Eksblowfish et Blowfish sont exactement les mêmes, la phase de calendrier clé de Eksblowfish s'assure que tout état ultérieur dépend à la fois du sel et de la clé (mot de passe de l'utilisateur), et qu'aucun état ne peut être précalculé à l'insu des deux. En raison de cette différence clé, bcrypt est un algorithme de hachage à sens unique. Vous ne pouvez pas récupérer le mot de passe en texte clair sans déjà connaître le sel, les tours et clé (mot de passe). [La source]

Comment utiliser bcrypt:

En utilisant PHP> = 5.5-DEV

Fonctions de hachage de mot ont maintenant été construits directement dans PHP> = 5.5. Vous pouvez maintenant utiliser password_hash() créer un bcrypt hash de n'importe quel mot de passe:

<?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

Pour vérifier un mot de passe fourni par l'utilisateur par rapport à un hachage existant, vous pouvez utiliser le password_verify() En tant que tel:

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

En utilisant PHP> = 5.3.7, <5.5-DEV (aussi RedHat PHP> = 5.3.3)

Il y a un bibliothèque de compatibilité sur GitHub créé en fonction du code source des fonctions ci-dessus écrites à l'origine en C, qui fournit la même fonctionnalité. Une fois la bibliothèque de compatibilité installée, l'utilisation est la même que ci-dessus (moins la notation de tableau de raccourcis si vous êtes toujours dans la branche 5.3.x).

En utilisant PHP <5.3.7 (PÉRIMÉ)

Vous pouvez utiliser crypt() fonction pour générer des hachages bcrypt de chaînes d'entrée. Cette classe peut générer automatiquement des sels et vérifier les hachages existants par rapport à une entrée. Si vous utilisez une version de PHP supérieure ou égale à 5.3.7, il est fortement recommandé d'utiliser la fonction intégrée ou la bibliothèque compat. Cette alternative est fournie uniquement à des fins historiques.

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;
  }
}

Vous pouvez utiliser ce code comme ceci:

$bcrypt = new Bcrypt(15);

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

Alternativement, vous pouvez également utiliser le Cadre de hachage PHP portable.


973
2018-06-12 19:23



Donc, vous voulez utiliser bcrypt? Impressionnant! Cependant, comme d'autres domaines de la cryptographie, vous ne devriez pas le faire vous-même. Si vous avez besoin de vous soucier de gérer des clés, de stocker des sels ou de générer des nombres aléatoires, vous vous trompez.

La raison est simple: il est si trivialement facile de bousiller bcrypt. En fait, si vous regardez presque chaque morceau de code sur cette page, vous remarquerez qu'il viole au moins un de ces problèmes communs.

Face It, la cryptographie est difficile.

Laissez-le pour les experts. Laissez les gens qui travaillent pour maintenir ces bibliothèques. Si vous devez prendre une décision, vous le faites mal.

Au lieu de cela, utilisez simplement une bibliothèque. Plusieurs existent en fonction de vos besoins.

Bibliothèques

Voici une répartition de certaines des API les plus courantes.

PHP 5.5 API - (Disponible pour 5.3.7+)

Depuis PHP 5.5, une nouvelle API pour le hachage des mots de passe est en cours d'introduction. Il y a aussi une bibliothèque de compatibilité de shim maintenue (par moi) pour 5.3.7+. Cela a l'avantage d'être un examen par les pairs et simple utiliser la mise en œuvre.

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
    }
}

Vraiment, il vise à être extrêmement simple.

Ressources:

Zend \ Crypt \ Mot de passe \ Bcrypt (5.3.2+)

C'est une autre API similaire à celle de PHP 5.5, et elle a un but similaire.

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
    }
}

Ressources:

PasswordLib

C'est une approche légèrement différente de hachage de mot de passe. Au lieu de simplement prendre en charge bcrypt, PasswordLib prend en charge un grand nombre d'algorithmes de hachage. Il est principalement utile dans les contextes où vous devez prendre en charge la compatibilité avec des systèmes hérités et disparates qui peuvent être hors de votre contrôle. Il supporte un grand nombre d'algorithmes de hachage. Et est soutenu 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
    }
}

Les références:

  • Code source / documentation: GitHub

PHPASS

C'est une couche qui supporte bcrypt, mais supporte aussi un algorithme assez fort qui est utile si vous n'avez pas accès à PHP> = 5.3.2 ... Il supporte en fait PHP 3.0+ (mais pas avec 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
    }
}

Ressources

Remarque: N'utilisez pas les alternatives PHPASS qui ne sont pas hébergées sur openwall, ce sont des projets différents !!!

À propos de la Colombie-Britannique

Si vous remarquez, chacune de ces bibliothèques renvoie une seule chaîne. C'est à cause de comment BCrypt fonctionne en interne. Et il y a une tonne de réponses à ce sujet. Voici une sélection que j'ai écrite, que je ne vais pas copier / coller ici, mais un lien vers:

Emballer

Il y a beaucoup de choix différents. Ce que vous choisissez est à vous. Cependant, je voudrais TRÈS recommande d'utiliser l'une des bibliothèques ci-dessus pour gérer ceci pour vous.

Encore une fois, si vous utilisez crypt() directement, vous faites probablement quelque chose de mal. Si votre code utilise hash() (ou md5() ou sha1()) directement, vous faites presque certainement quelque chose de mal.

Utilisez simplement une bibliothèque ...


273
2018-01-25 15:46



Vous aurez beaucoup d'informations dans Assez avec les tableaux Rainbow: Ce que vous devez savoir sur les régimes de mot de passe sécurisé ou Cadre de hachage de mot de passe PHP portable.

Le but est de hacher le mot de passe avec quelque chose de lent, donc quelqu'un obtenant votre base de données mourra en essayant de le forcer brutalement (un délai de 10 ms pour vérifier un mot de passe n'est rien pour vous). Bcrypt est lent et peut être utilisé avec un paramètre pour choisir à quel point il est lent.


43
2018-01-25 15:48



Vous pouvez créer un hachage à sens unique avec bcrypt en utilisant PHP crypt() fonctionner et passer dans un sel de Blowfish approprié. Le plus important de l'équation entière est que A) l'algorithme n'a pas été compromis et B) vous sel correctement chaque mot de passe. N'utilisez pas de sel à l'échelle de l'application; cela ouvre votre application entière pour attaquer à partir d'un seul ensemble de tables Rainbow.

PHP - Fonction de cryptage


34
2017-10-31 08:25




Edit: 2013.01.15 - Si votre serveur le supporte, utilisez la solution de martinstoeckli au lieu.


Tout le monde veut rendre cela plus compliqué que ça. La fonction crypt () fait la plupart du travail.

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);
}

Exemple:

$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

Je sais que cela devrait être évident, mais s'il vous plaît, n'utilisez pas le mot de passe comme mot de passe.


32
2018-01-11 08:07



La version 5.5 de PHP aura un support intégré pour BCrypt, les fonctions password_hash() et password_verify(). En fait ce ne sont que des wrappers autour de la fonction crypt()et doit faciliter l'utilisation correcte. Il prend soin de la génération d'un sel aléatoire sûr et fournit de bonnes valeurs par défaut.

La manière la plus simple d'utiliser ces fonctions sera:

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

Ce code va hacher le mot de passe avec BCrypt (algorithme 2y), génère un sel aléatoire à partir de la source aléatoire du système d'exploitation et utilise le paramètre de coût par défaut (10 pour le moment). La deuxième ligne vérifie si l'utilisateur a saisi un mot de passe correspondant à une valeur de hachage déjà stockée.

Si vous voulez changer le paramètre de coût, vous pouvez le faire comme ceci, en augmentant le paramètre de coût de 1, double le temps nécessaire pour calculer la valeur de hachage:

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

Contrairement à la "cost" paramètre, il est préférable d'omettre le "salt" paramètre, car la fonction fait déjà de son mieux pour créer un sel cryptographiquement sûr.

Pour la version 5.3.7 de PHP et plus tard, il existe un pack de compatibilité, du même auteur qui a fait le password_hash() fonction. Pour les versions PHP antérieures à 5.3.7, il n'y a pas de support pour crypt() avec 2y, l'algorithme BCrypt sécurisé unicode. On pourrait le remplacer à la place avec 2a, qui est la meilleure alternative pour les versions antérieures de PHP.


24
2018-02-19 14:17



Une alternative est d'utiliser scrypt, spécifiquement conçu pour être supérieur à bcrypt par Colin Percival dans son papier. Il y a un scrypt extension PHP dans PECL. Idéalement, cet algorithme serait déployé en PHP afin qu'il puisse être spécifié pour les fonctions password_ * (idéalement comme "PASSWORD_SCRYPT"), mais ce n'est pas encore le cas.


5
2017-12-07 20:56



Pensée actuelle: les hachages devraient être les plus lents disponibles, pas les plus rapides possibles. Cela supprime arc-en-table attaques.

Également lié, mais de précaution: Un attaquant ne devrait jamais avoir un accès illimité à votre écran de connexion. Pour éviter cela: Configurez une table de suivi d'adresse IP qui enregistre chaque hit avec l'URI. Si plus de 5 tentatives de connexion proviennent de la même adresse IP sur une période de cinq minutes, bloquez avec une explication. Une approche secondaire est d'avoir un schéma de mot de passe à deux niveaux, comme le font les banques. Mettre un lock-out pour les échecs sur le second passage augmente la sécurité.

Résumé: ralentir l'attaquant en utilisant des fonctions de hachage qui prennent du temps. En outre, bloquez trop d'accès à votre connexion et ajoutez un deuxième niveau de mot de passe.


4
2018-03-25 16:55



Pour OAuth 2 mots de passe:

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

2