Comment utiliser bcrypt pour Hasher les mots de passe en PHP?

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

mais qu'est-ce que bcrypt ? PHP n'offre pas de telles fonctions, Wikipedia babille sur un utilitaire de cryptage de fichiers et les recherches sur le Web ne révèlent que quelques implémentations de Blowfish dans différentes langues. Maintenant Blowfish est aussi disponible en PHP via mcrypt , mais comment cela aide-t-il à stocker les mots de passe? Blowfish est d'un usage général en chiffre, cela fonctionne de deux façons. S'il peut être crypté, il peut être décrypté. Les mots de passe besoin d'une fonction de hachage.

Quelle est l'explication?

1167
demandé sur Peter Mortensen 2011-01-25 18:34:04

9 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 multiples rounds font en sorte qu'un attaquant doit déployer des fonds et du matériel considérables pour pouvoir déchiffrer vos mots de passe. Ajoutez à cela par mot de passe sels ( bcrypt nécessite des sels) et vous pouvez être sûr qu'une attaque est virtuellement impossible sans un montant ridicule de fonds ou de matériel.

bcrypt utilise l'algorithme Eksblowfish pour hacher les mots de passe. Alors que la phase de cryptage de Eksblowfish et Blowfish sont exactement les mêmes, la phase de programmation clé de Eksblowfish garantit que tout état ultérieur dépend à la fois du sel et de la clé (mot de passe de l'utilisateur), et aucun État ne peut être précalculé sans que les deux ne soient connus. en raison de cette différence clé, bcrypt est un d'une manière algorithme de hachage. vous ne pouvez pas récupérer le mot de passe en clair sans connaître déjà le sel, rounds et la touche (mot de passe). [ Source ]

comment utiliser bcrypt:

utilisant PHP > = 5.5-DEV

les fonctions de hachage de mot de passe ont maintenant été directement intégrées dans PHP >= 5.5 . Vous pouvez maintenant utiliser password_hash() pour créer un bcrypt hachage de n'importe quel mot de passe:

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

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

pour vérifier un mot de passe fourni par l'utilisateur contre un hachage existant, vous pouvez utiliser le password_verify() comme tel:

<?php
// See the password_hash() example to see where this came from.
$hash = 'y$BCryptRequires22Chrcte/VlQH0piJtjXl.0t1XkA8pw9dMXTpOq';

if (password_verify('rasmuslerdorf', $hash)) {
    echo 'Password is valid!';
} else {
    echo 'Invalid password.';
}

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

il y a une bibliothèque de compatibilité sur GitHub créé sur la base du code source de la ci-dessus fonctions écrites à l'origine en C, qui fournit la même fonctionnalité. Une fois que la bibliothèque de compatibilité est installée, l'utilisation est la même que ci-dessus (moins la notation shorthand array si vous êtes toujours sur le 5.3.x succursale).

utilisant PHP < 5.3.7 (déprécié)

vous pouvez utiliser la fonction crypt() 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 hashes existants contre 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 solution n'est offerte qu'à 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('a$%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 portable PHP Hashing Framework .

988
répondu Andrew Moore 2018-04-19 19:10:00

alors, vous voulez utiliser bcrypt? génial! cependant, comme les autres domaines de la cryptographie, vous ne devriez pas le faire vous-même. Si vous avez besoin de vous soucier de n'importe quoi comme la gestion des clés, ou stocker des sels ou générer des nombres aléatoires, vous le faites mal.

la raison est simple: il est si trivialement facile de foirer bcrypt . En fait, si vous regardez presque chaque morceau de code sur cette page, vous remarquerez que c'est la violation au moins un de ces problèmes communs.

la cryptographie est dure.

c'est pour les experts. Laisser pour les personnes dont le travail est de maintenir ces bibliothèques. Si tu as besoin de prendre une décision, tu le fais mal.

à la place, il suffit d'utiliser une bibliothèque. Plusieurs existent selon vos besoins.

bibliothèques

Voici une répartition de certains des plus commune Api.

API PHP 5.5 - (Disponible pour 5.3.7+)

à partir de 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é shim maintenue (par me) pour 5.3.7+. Cela a l'avantage d'être un peer-reviewed et simple pour 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
    }
}

en fait, c'est très simple.

Ressources

:

Zend\Crypt\Mot De Passe\Bcrypt (5.3.2+)

C'est une autre API qui est similaire à celle de PHP 5.5, et qui fait un usage 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

il s'agit d'une approche légèrement différente du hachage de mot de passe. Au lieu de simplement supporter bcrypt, PasswordLib supporte un grand nombre d'algorithmes de hachage. Il est principalement utile dans les contextes où vous besoin de prendre en charge la compatibilité avec les systèmes existants et disparates qui peuvent être hors de votre contrôle. Il supporte un grand nombre d'algorithmes de hachage. Et est supporté 5.3.2 +

function register($username, $password) {
    $lib = new PasswordLib\PasswordLib();
    $hash = $lib->createPasswordHash($password, 'y$', 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:

PHPASS

c'est une couche qui supporte bcrypt, mais aussi une assez forte algorithme utile si vous n'avez pas accès à PHP >= 5.3.2... Il supporte 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

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

À Propos De BCrypt

Si vous remarquez, chacune de ces bibliothèques renvoie une chaîne unique. C'est à cause du fonctionnement interne de BCrypt. 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 lien vers:

Wrap Up

Il y a beaucoup de choix différents. Que vous choisissez est à vous. Cependant, je HIGHLY recommande que vous utilisiez l'une des bibliothèques ci-dessus pour gérer ceci pour vous.

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

il suffit d'utiliser une bibliothèque...

277
répondu ircmaxell 2017-10-17 16:53:19

vous obtiendrez beaucoup d'informations dans assez avec les tables arc-en-ciel: ce que vous devez savoir au sujet des systèmes de mots de passe sécurisés ou cadre Portable PHP password hashing .

le but est de hachez le mot de passe avec quelque chose de lent, de sorte que quelqu'un obtenir votre base de données de mot de passe va mourir en essayant de la force brute (un délai de 10 ms pour vérifier un mot de passe est rien pour vous, beaucoup pour quelqu'un qui essaie de force brute). Bcrypt est lent et peut être utilisé avec un paramètre pour choisir à quel point il est lent.

45
répondu Arkh 2016-05-23 18:12:43

vous pouvez créer un hachage à Sens Unique avec bcrypt en utilisant la fonction crypt() de PHP et en passant dans un sel de poisson-lune approprié. Le plus important de toute l'équation est que A) l'algorithme n'a pas été compromis et B) vous saler correctement chaque mot de passe . N'utilisez pas de sel à l'échelle de l'application; cela ouvre toute votre application pour attaquer à partir d'un seul ensemble de tables arc-en-ciel.

fonction PHP-Crypt

35
répondu coreyward 2013-01-11 08:21:07

Edit: 2013.01.15 - Si votre serveur prend en charge les, utiliser martinstoeckli la solution à la place.


" tout le monde veut rendre les choses plus compliquées qu'elles ne le sont. La fonction crypt () fait la plupart du travail.

function blowfishCrypt($password,$cost)
{
    $chars='./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    $salt=sprintf('y$%02d$',$cost);
//For PHP < PHP 5.3.7 use this instead
//    $salt=sprintf('a$%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 'mot de passe' comme votre mot de passe.

33
répondu Jon Hulka 2017-05-23 12:02:59

Version 5.5 de PHP aura prise en charge intégrée pour BCrypt, les fonctions password_hash() et password_verify() . En fait, ce ne sont que des enveloppements autour de la fonction crypt() , et doit le rendre plus facile à utiliser correctement. Il prend soin de la génération d'un sel aléatoire sûr, et fournit de bonnes valeurs par défaut.

la façon la plus facile d'utiliser ces fonctions sera:

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

ce code hachera le mot de passe avec BCrypt (algorithme 2y ), générera un sel aléatoire à partir de la source aléatoire du système D'exploitation, et utilisera le paramètre de coût par défaut (actuellement 10). La deuxième ligne vérifie si le mot de passe saisi par l'utilisateur correspond à 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 au paramètre "cost" , il est préférable d'omettre le paramètre "salt" , parce que la fonction fait déjà de son mieux pour créer un sel cryptographiquement sûr.

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

25
répondu martinstoeckli 2013-04-16 19:34:34

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

6
répondu Synchro 2014-02-19 14:17:36

pensée actuelle: les coups de fouet devraient être les plus lents disponibles, pas les plus rapides possibles. Ceci supprime les attaques rainbow table .

également lié, mais précaution: un attaquant ne devrait jamais avoir un accès illimité à votre écran de connexion. Pour éviter cela: mettez en place 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 dans une période de cinq minutes, bloquez avec explication. Une approche secondaire consiste à avoir un système de mots de passe à deux niveaux, comme le font les banques. Mettre un lock-out pour les échecs sur la deuxième passe renforce la sécurité.

résumé: ralentissez l'attaquant en utilisant des fonctions de hachage fastidieuses. Aussi, bloquez sur trop d'accès à votre login, et Ajouter un deuxième niveau de mot de passe.

6
répondu FYA 2014-04-13 17:10:07

pour OAuth 2 mots de passe:

$bcrypt = new \Zend\Crypt\Password\Bcrypt;
$bcrypt->create("youpasswordhere", 10)
3
répondu Shemeer M Ali 2016-05-23 18:16:20