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

1256

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

Mais c'est quoi bcrypt? PHP n'offre pas de telles fonctions, Wikipedia babille sur un utilitaire de cryptage de fichiers et les recherches sur le Web révèlent simplement 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 chiffrement à usage général, il fonctionne de deux manières. S'il peut être chiffré, il peut être déchiffré. Les mots de passe ont besoin d'une fonction de hachage unidirectionnelle.

Quelle est l'explication?

Vilx-
la source
13
Cette question a déjà été abordée et leur suggestion d'utiliser une bibliothèque standard est excellente. La sécurité est une question compliquée, et en utilisant un package conçu par quelqu'un qui sait ce qu'il fait, vous ne faites que vous aider.
eykanal
59
@eykanal - cette page ne mentionne même pas bcrypt, encore moins expliquer ce que c'est .
Vilx
8
@eykanal - Je ne demande pas d'explication sur son fonctionnement. Je veux juste savoir ce que c'est. Parce que tout ce que je peux trouver sur le net sous le mot-clé "bcrypt", ne peut en aucun cas être utilisé pour hacher des mots de passe. Pas directement de toute façon, et pas en PHP. OK, je comprends maintenant que c'est vraiment le paquet "phpass" qui utilise blowfish pour crypter votre mot de passe avec une clé qui est dérivée de votre mot de passe (en substance, crypter le mot de passe avec lui-même). Mais le référencer comme "bcrypt" est gravement trompeur, et c'est ce que je voulais clarifier dans cette question.
Vilx
3
@Vilx: J'ai ajouté plus d'informations sur la raison pour laquelle bcryptun algorithme de hachage unidirectionnel par rapport à un schéma de chiffrement dans ma réponse . Il y a toute cette idée fausse qui bcryptn'est que Blowfish alors qu'en fait, elle a un calendrier de clés totalement différent qui garantit que le texte brut ne peut pas être récupéré à partir du texte de chiffrement sans connaître l'état initial du chiffrement (sel, tours, clé).
Andrew Moore, le
1
Voir également le framework de hachage de mot de passe PHP portable d'Openwall (PHPass). Son durci contre un certain nombre d'attaques courantes contre les mots de passe des utilisateurs.
2014

Réponses:

1065

bcryptest un algorithme de hachage qui est évolutif avec du matériel (via un nombre configurable de tours). Sa lenteur et ses multiples tours garantissent qu'un attaquant doit déployer des fonds et du matériel massifs pour pouvoir casser vos mots de passe. Ajoutez à cela des sels par mot de passe ( bcryptnécessite des sels) et vous pouvez être sûr qu'une attaque est pratiquement impossible sans une somme ridicule de fonds ou de matériel.

bcryptutilise l' algorithme Eksblowfish pour hacher les mots de passe. Alors que la phase de chiffrement d' Eksblowfish et Blowfish sont exactement les mêmes, la phase de planification des clés d' Eksblowfish garantit que tout état ultérieur dépend à la fois du sel et de la clé (mot de passe utilisateur), et aucun état ne peut être précalculé à l'insu des deux. En raison de cette différence clé, il bcrypts'agit d'un algorithme de hachage unidirectionnel. Vous ne pouvez pas récupérer le mot de passe en texte brut sans connaître le sel, les tours et la clé (mot de passe). [ Source ]

Comment utiliser bcrypt:

En utilisant PHP> = 5.5-DEV

Les fonctions de hachage de mot de passe ont maintenant été intégrées directement dans PHP> = 5.5 . Vous pouvez maintenant utiliser password_hash()pour créer un bcrypthachage 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()comme 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 (également RedHat PHP> = 5.3.3)

Il existe une bibliothèque de compatibilité sur GitHub créée sur la base du code source des fonctions ci-dessus écrites à l'origine en C, qui fournit les mêmes fonctionnalités. Une fois la bibliothèque de compatibilité installée, l'utilisation est la même que ci-dessus (moins la notation du tableau abrégé si vous êtes toujours sur la branche 5.3.x).

Utilisation de PHP <5.3.7 (obsolète)

Vous pouvez utiliser la 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 n'est fournie 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('$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 framework de hachage PHP portable .

Andrew Moore
la source
7
@The Wicked Flea: Désolé de vous décevoir, mais mt_rand()est également défini en utilisant l'heure actuelle et l'ID de processus actuel. Veuillez voir GENERATE_SEED()dans/ext/standard/php_rand.h .
Andrew Moore
53
@Mike: Allez-y, c'est exactement pour cette raison!
Andrew Moore
14
Pour quiconque pense avoir besoin de modifier le début de la chaîne $ salt dans la fonction getSalt, ce n'est pas nécessaire. Le $ 2a $ __ fait partie du sel CRYPT_BLOWFISH. Extrait de la documentation: "Blowfish hachage avec un sel comme suit:" $ 2a $ ", un paramètre de coût à deux chiffres," $ "et 22 chiffres de l'alphabet".
jwinn
18
@MichaelLang: Une bonne chose crypt()est alors révisée par les pairs et vérifiée. Le code ci-dessus appelle PHP crypt(), qui appelle la crypt()fonction POSIX . Tout ce que le code ci-dessus fait de plus est de générer un sel aléatoire (qui n'a pas besoin d'être sécurisé cryptographiquement, le sel n'est pas considéré comme un secret) avant d'appeler crypt(). Vous devriez peut-être faire vous-même quelques recherches avant d'appeler le loup.
Andrew Moore
31
Veuillez noter que cette réponse, bien que bonne, commence à montrer son âge. Ce code (comme toute implémentation PHP sur laquelle il s'appuie crypt()) est soumis à une vulnérabilité de sécurité antérieure à 5.3.7, et est (très légèrement) inefficace après 5.3.7 - les détails du problème concerné peuvent être trouvés ici . Veuillez également noter que la nouvelle API de hachage de mot de passe ( compatibilité ascendante ) est désormais la méthode préférée pour implémenter le hachage de mot de passe bcrypt dans votre application.
DaveRandom
295

Alors, vous voulez utiliser bcrypt? Impressionnant! Cependant, comme dans d'autres domaines de la cryptographie, vous ne devriez pas le faire vous-même. Si vous devez 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 facile de visser 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 courants.

Avouons-le, la cryptographie est difficile.

Laissez-le aux experts. Laissez-le aux personnes dont le travail consiste à entretenir ces bibliothèques. Si vous devez prendre une décision, vous vous trompez.

Utilisez plutôt une bibliothèque. Plusieurs existent selon vos besoins.

Bibliothèques

Voici une ventilation de certaines des API les plus courantes.

API PHP 5.5 - (Disponible pour 5.3.7+)

Depuis PHP 5.5, une nouvelle API de hachage des mots de passe est en cours d'introduction. Il existe également une bibliothèque de compatibilité de shim maintenue (par moi) pour 5.3.7+. Cela a l'avantage d'être une implémentation évaluée par les pairs et simple à utiliser.

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 \ Password \ Bcrypt (5.3.2+)

Il s'agit d'une autre API similaire à celle de PHP 5.5 et remplit une fonction 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. Plutôt que 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 les systèmes hérités et disparates qui peuvent être hors de votre contrôle. Il prend en charge un grand nombre d'algorithmes de hachage. Et est pris en charge 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
    }
}

Références:

  • Code source / documentation: GitHub

PHPASS

C'est une couche qui prend en charge bcrypt, mais prend également en charge un algorithme assez puissant qui est utile si vous n'avez pas accès à PHP> = 5.3.2 ... Il prend en charge 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 BCrypt

Si vous remarquez, chacune de ces bibliothèques renvoie une seule chaîne. C'est à cause de la façon dont BCrypt fonctionne en interne. Et il y a des tonnes de réponses à ce sujet. Voici une sélection que j'ai écrite, que je ne copierai / collerai pas ici, mais un lien vers:

Emballer

Il existe de nombreux choix différents. Le choix vous appartient. Cependant, je vous recommande fortement d'utiliser l'une des bibliothèques ci-dessus pour gérer cela pour vous.

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

Utilisez simplement une bibliothèque ...

ircmaxell
la source
7
Le sel doit être généré de manière aléatoire, mais il n'a pas besoin de provenir d'une source aléatoire sécurisée. Le sel n'est pas un secret . Pouvoir deviner le prochain sel n'a pas de réel impact sur la sécurité; tant qu'ils proviennent d'un pool de données suffisamment important pour générer des sels différents pour chaque mot de passe encodé, tout va bien. N'oubliez pas que le sel est là pour empêcher l'utilisation de tables arc-en-ciel si vos hachis tombent entre de mauvaises mains. Ils ne sont pas secrets.
Andrew Moore
7
@AndrewMoore absolument correct! Cependant, le sel doit avoir suffisamment d'entropie pour être statistiquement unique. Pas seulement dans votre application, mais dans toutes les applications. A donc mt_rand()une période suffisamment élevée, mais la valeur de départ n'est que de 32 bits. Donc, l'utilisation mt_rand()vous limite efficacement à seulement 32 bits d'entropie. Ce qui, grâce au problème d'anniversaire, signifie que vous avez 50% de chances de collision avec seulement 7k de sels générés (globalement). Depuis bcryptaccepte 128 bits de sel, il est préférable d'utiliser une source qui peut fournir tous les 128 bits ;-). (à 128 bits, 50% de chances de collision se produisent lors des hachages 2e19) ...
ircmaxell
1
@ircmaxell: Hense le "pool de données suffisamment grand". Cependant, votre source ne doit pas être une source d'entropie TRÈS ÉLEVÉE, juste assez élevée pour les 128 bits. Cependant, si vous avez épuisé toutes vos sources disponibles (ne disposez pas d'OpenSSL, etc ...) et que votre seul repli est mt_rand (), c'est toujours mieux que l'alternative (qui est rand ()).
Andrew Moore
4
@AndrewMoore: absolument. Ne dis pas ça. Juste que mt_randet uniqid(et donc lcg_valueet rand) ne sont pas des premiers choix ...
ircmaxell
1
ircmaxell, merci beaucoup pour la bibliothèque password_compat pour 5.3.xx, nous n'en avions pas besoin auparavant mais maintenant nous le faisons, sur un serveur php 5.3.xx, et merci pour vos conseils clairs de ne pas essayer de faire cette logique soi-même.
Lizardx
47

Vous obtiendrez beaucoup d'informations dans Enough With The Rainbow Tables: Ce que vous devez savoir sur les schémas de mot de passe sécurisés ou le framework 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 qui obtient votre base de données de mots de passe 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, beaucoup pour quelqu'un qui essaie de le forcer brutalement). Bcrypt est lent et peut être utilisé avec un paramètre pour choisir sa lenteur.

Arkh
la source
7
Appliquez ce que vous voulez, les utilisateurs réussiront à bousiller et à utiliser le même mot de passe sur plusieurs choses. Vous devez donc le protéger autant que possible ou implémenter quelque chose qui vous permet de ne pas stocker de mot de passe (SSO, openID etc.).
Arkh
41
Le hachage de mot de passe est utilisé pour se protéger contre une attaque: quelqu'un a volé votre base de données et souhaite obtenir une connexion en texte clair + des mots de passe.
Arkh
4
@Josh K. Je vous encourage à essayer de casser quelques mots de passe simples après les avoir réglés sur phpass, il faut donc entre 1 ms et 10 ms pour le calculer sur votre serveur Web.
Arkh
3
D'accord. Mais le type d'utilisateur qui utilisera qwerty comme mot de passe est également le type d'utilisateur qui marquera tout utilisateur compliqué quelque part qu'il (et les attaquants) pourront facilement le lire. Ce que l'utilisation de bcrypt accomplit, c'est que lorsque votre base de données sera rendue publique contre votre volonté, il sera plus difficile d'accéder à ces utilisateurs qui ont un mot de passe comme ^ | $$ & ZL6- £ que si vous avez utilisé sha512 en une seule passe.
Arkh
4
@coreyward vaut la peine de noter que cela est plus dangereux que de ne pas bloquer du tout; qui est facilement considéré comme un vecteur de "déni de service". Il vous suffit de commencer à envoyer des messages indésirables sur tous les comptes connus et vous pouvez perturber très, très facilement de nombreux utilisateurs. Il est préférable de cibler (retarder) l'attaquant que de lui refuser carrément l'accès, surtout s'il s'agit d'un client payant.
damianb
36

Vous pouvez créer un hachage unidirectionnel avec bcrypt en utilisant la crypt()fonction PHP et en passant un sel Blowfish approprié. Le plus important de toute l'équation est que A) l'algorithme n'a pas été compromis et B) vous salez correctement chaque mot de passe . N'utilisez pas de sel pour toute l'application; qui ouvre toute votre application pour attaquer à partir d'un seul ensemble de tables Rainbow.

PHP - Fonction Crypt

coreyward
la source
4
C'est la bonne approche - utilisez la crypt()fonction PHP , qui prend en charge plusieurs fonctions de hachage de mot de passe différentes. Assurez-vous que vous n'utilisez pas CRYPT_STD_DESou CRYPT_EXT_DES- l'un des autres types pris en charge est correct (et inclut bcrypt, sous le nom CRYPT_BLOWFISH).
caf
4
SHA a en effet également un paramètre de coût, via l'option «rounds». En utilisant cela, je ne vois aucune raison de favoriser bcrypt.
Pieter Ennes
3
En fait, un seul SHA-1 (ou MD5) d'un mot de passe est toujours facilement capable de force brute, avec ou sans sel (le sel aide contre les tables arc-en-ciel, pas contre le forçage brutal). Utilisez bcrypt.
Paŭlo Ebermann
Je trouve troublant que tout le monde semble dire "bcrypt" quand ils veulent dire crypt () de php.
Sliq
3
@Panique Pourquoi? L'algorithme est appelé bcrypt . cryptexpose plusieurs hachages de mot de passe, avec bcrypt correspondant à la CRYPT_BLOWFISHconstante. Bcrypt est actuellement l'algorithme le plus puissant pris en charge par cryptet plusieurs autres qu'il prend en charge sont assez faibles.
CodesInChaos
34

Edit: 2013.01.15 - Si votre serveur le supporte, utilisez plutôt la solution de martinstoeckli .


Tout le monde veut rendre cela plus compliqué qu'il ne l'est. 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 veuillez ne pas utiliser le mot de passe comme mot de passe.

Jon Hulka
la source
3
La création du sel pourrait être améliorée (utiliser la source aléatoire de l'OS), sinon ça me semble bien. Pour les versions PHP plus récentes, il est préférable d'utiliser à la 2yplace de 2a.
martinstoeckli
utiliser mcrypt_create_iv($size, MCRYPT_DEV_URANDOM)comme source pour le sel.
CodesInChaos
J'examinerai de plus près mcrypt_create_iv () quand j'aurai un instant, si rien d'autre ne devrait améliorer légèrement les performances.
Jon Hulka
2
Ajoutez le codage Base64 et traduisez-le aux utilisations de l'alphabet personnalisé bcrypt. mcrypt_create_iv(17, MCRYPT_DEV_URANDOM), str_replace('+', '.', base64_encode($rawSalt)),$salt = substr($salt, 0, 22);
CodesInChaos
1
@JonHulka - Jetez un œil au pack de compatibilité de PHP [Ligne 127], il s'agit d'une implémentation simple.
martinstoeckli
29

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 il sera plus facile de l'utiliser correctement. Il prend en charge la génération d'un sel aléatoire sûr et fournit de bonnes valeurs par défaut.

La façon la plus simple 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è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 (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 souhaitez modifier 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 "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 PHP version 5.3.7 et versions ultérieures, il existe un pack de compatibilité , du même auteur qui a créé la password_hash()fonction. Pour les versions PHP antérieures à 5.3.7, il n'y a pas de support pour crypt()with 2y, l'algorithme BCrypt sécurisé unicode. On pourrait le remplacer à la place par 2a, ce qui est la meilleure alternative pour les versions PHP antérieures.

martinstoeckli
la source
3
Après avoir lu ceci, ma première pensée a été "comment stockez-vous le sel généré"? Après avoir fouillé dans les documents, la fonction password_hash () finit par générer une chaîne qui stocke la méthode de chiffrement, le sel et le hachage généré. Ainsi, il stocke juste tout ce dont il a besoin dans une chaîne pour que la fonction password_verify () fonctionne. Je voulais juste mentionner cela car cela peut aider les autres lorsqu'ils voient cela.
jzimmerman2011
@ jzimmerman2011 - Exactement, dans une autre réponse, j'ai essayé d'expliquer ce format de stockage avec un exemple.
martinstoeckli
7

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

Également lié, mais par précaution: un attaquant ne devrait jamais avoir un accès illimité à votre écran de connexion. Pour éviter cela: configurez une table de suivi des adresses IP qui enregistre chaque occurrence avec l'URI. Si plus de 5 tentatives de connexion proviennent de la même adresse IP au cours d'une période de cinq minutes, bloquez avec explication. Une approche secondaire consiste à avoir un système de mot de passe à deux niveaux, comme les banques. Mettre un verrouillage pour les échecs sur la deuxième passe renforce la sécurité.

Résumé: ralentissez l'attaquant en utilisant des fonctions de hachage chronophages. De plus, bloquez trop d'accès à votre connexion et ajoutez un deuxième niveau de mot de passe.

FYA
la source
Je pense qu'ils supposent que l'attaquant a déjà réussi à voler ma base de données par un autre moyen, et essaie maintenant de sortir les mots de passe afin de les essayer sur paypal ou quelque chose.
Vilx
4
À mi-chemin de 2012 et cette réponse est toujours bancale, comment un algorithme de hachage lent empêche-t-il les attaques de table arc-en-ciel? Je pensais qu'un sel de plage d'octets aléatoire faisait? J'ai toujours pensé que la vitesse de l'algorithme de hachage dicte le nombre d'itérations qu'ils peuvent envoyer contre le hachage qu'ils vous ont formé dans un laps de temps spécifique. De plus, NE JAMAIS BLOQUER UN UTILISATEUR SUR LES TENTATIVES DE CONNEXION ÉCHOUÉES, croyez-moi, vos utilisateurs en auront marre, souvent sur certains sites, je dois me connecter près de 5 fois parfois plus avant de me souvenir de mon mot de passe. De plus, le deuxième niveau de passe ne fonctionne pas, l'authentification en deux étapes avec le code de téléphone mobile pourrait cependant.
Sammaye
1
@Sammaye, je serais d'accord avec cela jusqu'à un certain point. J'ai configuré un bloc sur 5 tentatives de connexion échouées, avant de le relever rapidement à 7, puis 10 maintenant il est assis sur 20. Aucun utilisateur normal ne devrait avoir 20 tentatives de connexion échouées mais son assez bas pour arrêter facilement les attaques par force brute
Bruce Aldridge
@BruceAldridge Personnellement, je pense qu'il serait préférable de faire une pause aléatoire de votre script après, disons, 7 échecs de connexion et d'afficher un captcha plutôt que de bloquer. Le blocage est une décision très agressive à prendre.
Sammaye
1
@Sammaye Je suis d'accord que les blocs permanents sont mauvais. Je fais référence à un blocage temporaire qui augmente avec le nombre de tentatives infructueuses.
Bruce Aldridge
7

Voici une réponse mise à jour à cette vieille question!

La bonne façon de hacher les mots de passe en PHP depuis 5.5 est avec password_hash(), et la bonne façon de les vérifier est avec password_verify(), et cela est toujours vrai en PHP 8.0. Ces fonctions utilisent des hachages bcrypt par défaut, mais d'autres algorithmes plus puissants ont été ajoutés. Vous pouvez modifier le facteur de travail (en fait, la force du cryptage) via les password_hashparamètres.

Cependant, bien qu'il soit encore assez fort, bcrypt n'est plus considéré comme à la pointe de la technologie ; un meilleur ensemble d'algorithmes de hachage de mot de passe est arrivé appelé Argon2 , avec les variantes Argon2i, Argon2d et Argon2id. La différence entre eux (comme décrit ici ):

Argon2 a une variante principale: Argon2id et deux variantes supplémentaires: Argon2d et Argon2i. Argon2d utilise un accès à la mémoire dépendant des données, ce qui le rend approprié pour les crypto-monnaies et les applications de preuve de travail sans menaces d'attaques de synchronisation du canal latéral. Argon2i utilise un accès à la mémoire indépendant des données, ce qui est préférable pour le hachage de mot de passe et la dérivation de clé basée sur un mot de passe. Argon2id fonctionne comme Argon2i pour la première moitié de la première itération sur la mémoire, et comme Argon2d pour le reste, offrant ainsi à la fois une protection contre les attaques par canal latéral et des économies de force brute en raison des compromis de temps-mémoire.

Le support Argon2i a été ajouté en PHP 7.2, et vous le demandez comme ceci:

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

et le support Argon2id a été ajouté en PHP 7.3:

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

Aucune modification n'est requise pour vérifier les mots de passe car la chaîne de hachage résultante contient des informations sur l'algorithme, le sel et les facteurs de travail utilisés lors de sa création.

Tout à fait séparément (et quelque peu redondant), libsodium (ajouté en PHP 7.2) fournit également le hachage Argon2 via les fonctions sodium_crypto_pwhash_str ()et sodium_crypto_pwhash_str_verify(), qui fonctionnent à peu près de la même manière que les fonctions intégrées de PHP. Une des raisons possibles de leur utilisation est que PHP peut parfois être compilé sans libargon2, ce qui rend les algorithmes Argon2 indisponibles pour la fonction password_hash; PHP 7.2 et supérieur devraient toujours avoir libsodium activé, mais ce n'est peut-être pas le cas - mais au moins il y a deux façons d'obtenir cet algorithme. Voici comment vous pouvez créer un hachage Argon2id avec libsodium (même en PHP 7.2, qui, autrement, ne prend pas en charge Argon2id)):

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

Notez qu'il ne vous permet pas de spécifier un sel manuellement; cela fait partie de la philosophie de libsodium - ne permettez pas aux utilisateurs de définir des paramètres qui pourraient compromettre la sécurité - par exemple, rien ne vous empêche de passer une chaîne de sel vide à la password_hashfonction PHP ; libsodium ne vous laisse rien faire de si bête!

Synchro
la source
4

Pour les mots de passe OAuth 2 :

$bcrypt = new \Zend\Crypt\Password\Bcrypt;
$bcrypt->create("youpasswordhere", 10)
Shemeer M Ali
la source
1

Comme nous le savons tous, le stockage du mot de passe en texte clair dans la base de données n'est pas sécurisé. le bcrypt est une technique de hachage de mot de passe. l'une des fonctions étonnantes de bcrypt est qu'il nous protège des pirates informatiques, il est utilisé pour protéger le mot de passe contre les attaques de piratage, car le mot de passe est stocké sous forme bcryptée.

la fonction password_hash () est utilisée pour créer un nouveau hachage de mot de passe. Il utilise un algorithme de hachage fort et robuste. La fonction password_hash () est très compatible avec la fonction crypt (). Par conséquent, les hachages de mot de passe créés par crypt () peuvent être utilisés avec password_hash () et vice-versa. Les fonctions password_verify () et password_hash () se limitent aux wrappers autour de la fonction crypt () et facilitent son utilisation avec précision.

SYNTAXE

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

Les algorithmes suivants sont actuellement pris en charge par la fonction password_hash ():

PASSWORD_DEFAULT PASSWORD_BCRYPT PASSWORD_ARGON2I PASSWORD_ARGON2ID

Paramètres: Cette fonction accepte trois paramètres tels que mentionnés ci-dessus et décrits ci-dessous:

mot de passe : il stocke le mot de passe de l'utilisateur. algo : C'est la constante de l'algorithme de mot de passe qui est utilisée en continu tout en désignant l'algorithme à utiliser lors du hachage du mot de passe. options : Il s'agit d'un tableau associatif, qui contient les options. Si cela est supprimé et ne comprend pas, un sel aléatoire sera utilisé et l'utilisation d'un coût par défaut se produira. Valeur de retour : il renvoie le mot de passe haché en cas de succès ou False en cas d'échec.

Exemple :

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

Les programmes ci-dessous illustrent la fonction password_hash () en PHP:

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

PRODUCTION

$2y$10$Z166W1fBdsLcXPVQVfPw/uRq1ueWMA6sLt9bmdUFz9AmOGLdM393G

Nayab Muhammad
la source