web-dev-qa-db-fra.com

Comment puis-je stocker les mots de passe de mes utilisateurs en toute sécurité?

Combien plus sûr est-ce que plain MD5 ? Je viens de commencer à étudier la sécurité par mot de passe. Je suis assez nouveau en PHP.

$salt = 'csdnfgksdgojnmfnb';

$password = md5($salt.$_POST['password']);
$result = mysql_query("SELECT id FROM users
                       WHERE username = '".mysql_real_escape_string($_POST['username'])."'
                       AND password = '$password'");

if (mysql_num_rows($result) < 1) {
    /* Access denied */
    echo "The username or password you entered is incorrect.";
} 
else {
    $_SESSION['id'] = mysql_result($result, 0, 'id');
    #header("Location: ./");
    echo "Hello $_SESSION[id]!";
}
166
Rebar

Le moyen le plus simple de sécuriser votre schéma de stockage de mots de passe consiste à utiliser une bibliothèque standard.

Étant donné que la sécurité tend à être beaucoup plus compliquée et que les possibilités de vissage sont plus invisibles que la plupart des programmeurs ne pourraient le faire, l'utilisation d'une bibliothèque standard est presque toujours la solution la plus simple et la plus sécurisée (sinon la seule).


La nouvelle API de mot de passe PHP (5.5.0+)

Si vous utilisez PHP version 5.5.0 ou plus récente, vous pouvez utiliser la nouvelle API de hachage de mot de passe simplifié.

Exemple de code utilisant l'API de mot de passe de PHP:

<?php
// $hash is what you would store in your database
$hash = password_hash($_POST['password'], PASSWORD_DEFAULT, ['cost' => 12]);

// $hash would be the $hash (above) stored in your database for this user
$checked = password_verify($_POST['password'], $hash);
if ($checked) {
    echo 'password correct';
} else {
    echo 'wrong credentials';
}

(Si vous utilisez toujours l'ancienne version 5.3.7 ou une version plus récente, vous pouvez installer ircmaxell/password_compat pour avoir accès aux fonctions intégrées)


Améliorer le hachis salé: ajouter du poivre

Si vous voulez plus de sécurité, les responsables de la sécurité maintenant (2017) recommandent d'ajouter un ' pepper ' aux mots de passe hachés (automatiquement). 

Il existe une solution simple, qui permet d’implémenter ce modèle de manière sécurisée, je recommande: Netsilik/PepperedPasswords ( github ).
Il est fourni avec une licence MIT, vous pouvez donc l'utiliser comme vous le souhaitez, même dans des projets propriétaires.

Exemple de code utilisant Netsilik/PepperedPasswords:

<?php
use Netsilik/Lib/PepperedPasswords;

// Some long, random, binary string, encoded as hexadecimal; stored in your configuration (NOT in your Database, as that would defeat the entire purpose of the pepper).
$config['pepper'] = hex2bin('012345679ABCDEF012345679ABCDEF012345679ABCDEF012345679ABCDEF');

$hasher = new PepperedPasswords($config['pepper']);

// $hash is what you would store in your database
$hash = $hasher->hash($_POST['password']);

// $hash would be the $hash (above) stored in your database for this user
$checked = $hasher->verify($_POST['password'], $hash);
if ($checked) {
    echo 'password correct';
} else {
    echo 'wrong credentials';
}


La bibliothèque standard OLD

Remarque: vous ne devriez plus en avoir besoin! Ceci est seulement ici à des fins historiques.

Examinez: Portable PHP le cadre de hachage du mot de passe : phpass et assurez-vous d'utiliser l'algorithme CRYPT_BLOWFISH dans la mesure du possible.

Exemple de code utilisant phpass (v0.2):

<?php
require('PasswordHash.php');

$pwdHasher = new PasswordHash(8, FALSE);

// $hash is what you would store in your database
$hash = $pwdHasher->HashPassword( $password );

// $hash would be the $hash (above) stored in your database for this user
$checked = $pwdHasher->CheckPassword($password, $hash);
if ($checked) {
    echo 'password correct';
} else {
    echo 'wrong credentials';
}

PHPass a été implémenté dans des projets assez connus:

  • phpbb3
  • WordPress 2.5+ ainsi que bbPress
  • la version 7 de Drupal (module disponible pour Drupal 5 et 6)
  • autres

La bonne chose à faire est que vous n'avez pas à vous soucier des détails, ceux-ci ont été programmés par des personnes expérimentées et revus par de nombreuses personnes sur Internet.

Pour plus d'informations sur les méthodes de stockage de mot de passe, lisez Jeff `s blog post: Vous ne stockez probablement pas les mots de passe de manière incorrecte

Quoi que vous fassiez si vous optez pour l'approche 'je le ferai moi-même, merci}', n'utilisez plus MD5 ni SHA1. Ce sont des algorithmes de hachage de Nice, mais considérés comme cassés pour des raisons de sécurité.

Actuellement, l’utilisation de crypt avec CRYPT_BLOWFISH est la meilleure pratique.
CRYPT_BLOWFISH dans PHP est une implémentation du hachage Bcrypt. Bcrypt est basé sur le chiffrement par blocs de Blowfish, utilisant sa configuration de clé coûteuse pour ralentir l'algorithme.

262
Jacco

Vos utilisateurs seront beaucoup plus en sécurité si vous utilisez des requêtes paramétrées au lieu de concaténer des instructions SQL. Et le sel doit être unique pour chaque utilisateur et doit être stocké avec le hachage du mot de passe.

28
Anton Gogolev

Un meilleur moyen serait que chaque utilisateur ait un sel unique.

L'avantage d'un sel est qu'il est plus difficile pour un attaquant de pré-générer la signature MD5 de chaque mot du dictionnaire. Mais si un attaquant découvre que vous avez un sel fixe, il peut alors pré-générer la signature MD5 de chaque mot du dictionnaire précédé du préfixe.

Un meilleur moyen consiste à chaque fois qu'un utilisateur modifie son mot de passe, votre système génère un sel aléatoire et le stocke avec l'enregistrement de l'utilisateur. La vérification du mot de passe coûte un peu plus cher (puisque vous devez rechercher le sel avant de pouvoir générer la signature MD5), mais il est beaucoup plus difficile pour un attaquant de pré-générer des MD5.

11
R Samuel Klatchko

Avec PHP 5.5 (ce que je décris est disponible pour les versions les plus anciennes, voir ci-dessous), j'aimerais vous suggérer d'utiliser sa nouvelle solution intégrée: password_hash() et password_verify() . Il offre plusieurs options pour atteindre le niveau de sécurité requis par mot de passe (par exemple, en spécifiant un paramètre "cost" dans le tableau $options).

<?php
var_dump(password_hash("my-secret-password", PASSWORD_DEFAULT));

$options = array(
    'cost' => 7, // this is the number of rounds for bcrypt
    // 'salt' => 'TphfsM82o1uEKlfP9vf1f', // you could specify a salt but it is not recommended
);
var_dump(password_hash("my-secret-password", PASSWORD_BCRYPT, $options));
?>

reviendra

string(60) "$2y$10$w2LxXdIcqJpD6idFTNn.eeZbKesdu5y41ksL22iI8C4/6EweI7OK."
string(60) "$2y$07$TphfsM82o1uEKlfP9vf1fOKohBqGVXOJEmnUtQu7Y1UMft1R4D3d."

Comme vous pouvez le constater, la chaîne contient le sel ainsi que le coût spécifié dans les options. Il contient également l'algorithme utilisé.

Par conséquent, lors de la vérification du mot de passe (par exemple lorsque l'utilisateur se connecte), lors de l'utilisation de la fonction complémentaire password_verify(), il extraira les paramètres de chiffrement nécessaires du hachage du mot de passe lui-même.

Lorsque vous ne spécifiez pas de sel, le hachage de mot de passe généré sera différent à chaque appel de password_hash() car le sel est généré de manière aléatoire. Par conséquent, la comparaison d'un hash précédent avec un nouveau généré va échouer, même avec un mot de passe correct.

La vérification fonctionne comme ceci:

var_dump(password_verify("my-secret-password", '$2y$10$BjHJbMCNWIJq7xiAeyFaHOGaO0jjNoE11e0YAer6Zu01OZHN/gk6K'));
var_dump(password_verify("wrong-password", '$2y$10$BjHJbMCNWIJq7xiAeyFaHOGaO0jjNoE11e0YAer6Zu01OZHN/gk6K'));

var_dump(password_verify("my-secret-password", '$2y$07$TphfsM82o1uEKlfP9vf1fOKohBqGVXOJEmnUtQu7Y1UMft1R4D3d.'));
var_dump(password_verify("wrong-password", '$2y$07$TphfsM82o1uEKlfP9vf1fOKohBqGVXOJEmnUtQu7Y1UMft1R4D3d.'));

J'espère que ces fonctions intégrées fourniront bientôt une meilleure sécurité des mots de passe en cas de vol de données, car cela réduit la quantité de réflexion que le programmeur doit mettre dans une implémentation appropriée.

Il y a une petite bibliothèque (un fichier PHP) qui vous donnera le password_hash de PHP 5.5 dans PHP 5.3.7+: https://github.com/ircmaxell/ password_compat

11
akirk

Ça me va. M. Atwood a écrit à propos de la force du MD5 contre les tables Rainbow et, en gros, avec un long sel comme celui-ci, vous êtes bien assis (même si une ponctuation aléatoire/des chiffres pourraient l’améliorer).

Vous pouvez également regarder SHA-1, qui semble devenir de plus en plus populaire ces jours-ci.

0
nickf

Je veux ajouter:

  • Ne pas limiter les mots de passe des utilisateurs par longueur

Pour des raisons de compatibilité avec les anciens systèmes, définissez souvent une limite pour la longueur maximale du mot de passe. C'est une mauvaise politique de sécurité: si vous définissez une restriction, définissez-la uniquement pour la longueur minimale des mots de passe.

  • Ne pas envoyer les mots de passe des utilisateurs par courrier électronique

Pour récupérer un mot de passe oublié, vous devez envoyer l'adresse avec laquelle l'utilisateur peut changer le mot de passe.

  • Mettre à jour les hachages des mots de passe des utilisateurs

Le mot de passe hash peut être obsolète (les paramètres de l'algorithme peuvent être mis à jour). En utilisant la fonction password_needs_rehash() vous pouvez y accéder.

0
Aleksey Bykov