171 votes

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

C'est beaucoup plus sûr que la simple MD5 ? Je viens de commencer à m'intéresser à la sécurité des mots de passe. Je suis assez novice en matière de 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]!";
}

0 votes

Note : php 5.4+ intègre cette fonctionnalité.

0 votes

Voir également la page de l'Openwall Cadre de hachage de mots de passe en PHP (PHPass). Il est portable et renforcé contre un certain nombre d'attaques courantes sur les mots de passe des utilisateurs.

1 votes

Obligatoirement "utiliser AOPs au lieu de l'interpolation de chaînes", pour les personnes qui tombent sur cette question aujourd'hui.

273voto

Jacco Points 12528

Le moyen le plus simple de sécuriser votre système de stockage de mots de passe consiste à en utilisant une bibliothèque standard .

Étant donné que la sécurité est généralement beaucoup plus compliquée et que les possibilités d'erreurs invisibles sont plus nombreuses que celles auxquelles la plupart des programmeurs pourraient s'attaquer seuls, l'utilisation d'une bibliothèque standard est presque toujours l'option la plus simple et la plus sûre (si ce n'est la seule).

La nouvelle API PHP pour les mots de passe (5.5.0+)

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

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 la version 5.3.7 ou une version plus récente, vous pouvez installer le logiciel suivant ircmaxell/password_compat pour avoir accès aux fonctions intégrées)

Améliorer les hachages salés : ajouter du poivre

Si vous voulez une sécurité supplémentaire, les spécialistes de la sécurité recommandent maintenant (2017) d'ajouter un ' poivre ' aux hachages de mots de passe (automatiquement) salés.

Il existe une classe simple qui implémente ce modèle de manière sécurisée, que je recommande : Netsilik/Mots de passe poivrés ( github ).
Il est livré avec une licence MIT, vous pouvez donc l'utiliser comme bon vous semble, 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

Veuillez noter : vous ne devriez plus avoir besoin de ça ! C'est seulement ici pour des raisons historiques.

Jetez un coup d'oeil : Cadre portable de hachage de mots de passe en PHP : phpass et assurez-vous d'utiliser le CRYPT_BLOWFISH algorithme si 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 certains projets bien connus :

  • phpBB3
  • WordPress 2.5+ ainsi que bbPress
  • la version Drupal 7, (module disponible pour Drupal 5 & 6)
  • autres

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

Pour plus d'informations sur les systèmes de stockage des mots de passe, lisez Jeff `s blog post : Vous stockez probablement vos mots de passe de manière incorrecte.

Quoi qu'il en soit, si vous optez pour le Je vais le faire moi-même, merci. approche, ne pas utiliser MD5 o SHA1 plus . Ce sont de bons algorithmes de hachage, mais ils sont considérés comme étant brisé pour des raisons de sécurité .

Actuellement, l'utilisation de crypte avec CRYPT_BLOWFISH est la meilleure pratique.
CRYPT_BLOWFISH en PHP est une implémentation du hachage Bcrypt. Bcrypt est basé sur l'algorithme de chiffrement par blocs Blowfish, qui utilise une clé coûteuse pour ralentir l'algorithme.

30voto

Anton Gogolev Points 59794

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.

1 votes

Il existe un bon article sur la sécurité en PHP sur Nettuts+, le salage des mots de passe est également mentionné. Vous devriez peut-être y jeter un coup d'œil : net.tutsplus.com/tutorials/php/

3 votes

Le Nettuts+ est un très mauvais article à utiliser comme modèle - il inclut l'utilisation du MD5 qui peut être forcé très facilement, même avec un sel. À la place, utilisez simplement la bibliothèque PHPass qui est bien, bien meilleure que n'importe quel code que vous pouvez trouver sur un site de tutoriel, c'est-à-dire cette réponse : stackoverflow.com/questions/1581610/

12voto

R Samuel Klatchko Points 44549

Une meilleure solution serait que chaque utilisateur dispose d'un sel unique.

L'avantage d'avoir 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 apprend que vous avez un sel fixe, il pourrait alors pré-générer la signature MD5 de chaque mot du dictionnaire préfixé par votre sel fixe.

Une meilleure solution serait qu'à chaque fois qu'un utilisateur change son mot de passe, votre système génère un sel aléatoire et le stocke avec l'enregistrement de l'utilisateur. Cela rend la vérification du mot de passe un peu plus coûteuse (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.

3 votes

Les sels sont généralement stockés avec le hachage du mot de passe (par exemple, la sortie de la commande crypt() fonction). Et puisque vous devez de toute façon récupérer le hachage du mot de passe, l'utilisation d'un sel spécifique à l'utilisateur ne rendra pas la procédure plus coûteuse. (Ou vous voulez dire que la génération d'un nouveau sel aléatoire est coûteuse ? Je ne le pense pas vraiment). Sinon, +1.

0 votes

Pour des raisons de sécurité, vous pouvez souhaiter que l'accès à la table ne se fasse que par le biais de procédures stockées et que le hachage ne soit jamais renvoyé. Au lieu de cela, le client transmet ce qu'il pense être le hachage et obtient un indicateur de succès ou d'échec. Cela permet à la procédure stockée d'enregistrer la tentative, de créer une session, etc.

0 votes

@Inshallah - si tous les utilisateurs ont le même sel, alors vous pouvez réutiliser l'attaque par dictionnaire que vous utilisez sur l'utilisateur 1 contre l'utilisateur 2. Mais si chaque utilisateur a un sel unique, vous devrez générer un nouveau dictionnaire pour chaque utilisateur que vous voulez attaquer.

12voto

akirk Points 3338

Avec PHP 5.5 (ce que je décris est disponible même pour les versions antérieures, voir ci-dessous), je voudrais suggérer d'utiliser sa nouvelle solution intégrée : password_hash() y password_verify() . Il fournit plusieurs options afin d'atteindre le niveau de sécurité du mot de passe dont vous avez besoin (par exemple en spécifiant un paramètre "coût" par le biais de l'option $options réseau)

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

retournera

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 qui a été spécifié dans les options. Elle 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 du complimentaire password_verify() il extraira les paramètres cryptographiques nécessaires à partir du hachage du mot de passe lui-même.

Si l'on ne spécifie pas de sel, le hachage du mot de passe généré sera différent à chaque appel de la commande password_hash() car le sel est généré de façon aléatoire. Par conséquent, la comparaison d'un hachage précédent avec un hachage nouvellement généré échouera, même pour un mot de passe correct.

La vérification fonctionne comme suit :

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 la mise à disposition de ces fonctions intégrées permettra bientôt de mieux sécuriser les mots de passe en cas de vol de données, car elle réduit la quantité de réflexion que le programmeur doit consacrer à une mise en œuvre correcte.

Il existe une petite bibliothèque (un seul fichier PHP) qui vous permettra d'utiliser PHP 5.5. password_hash en PHP 5.3.7+ : https://github.com/ircmaxell/password_compat

2 votes

Dans la plupart des cas, il est préférable d'omettre le paramètre "sel". La fonction crée un sel à partir de la source aléatoire du système d'exploitation, il y a très peu de chance que vous puissiez fournir un meilleur sel par vous-même.

1 votes

C'est ce que j'ai écrit, n'est-ce pas ? "si aucun sel n'est spécifié, il est généré aléatoirement, pour cette raison il est préférable de ne pas spécifier de sel"

0 votes

La plupart des exemples montrent comment ajouter les deux paramètres, même lorsqu'il n'est pas recommandé d'ajouter un sel, alors je me demande pourquoi ? Et pour être honnête, je ne lis que le commentaire derrière le code, pas sur la ligne suivante. De toute façon, ne serait-il pas mieux que l'exemple montre comment utiliser au mieux la fonction ?

0voto

nickf Points 185423

C'est bon pour moi. M. Atwood a écrit sur la force du MD5 par rapport aux tables arc-en-ciel et, en gros, avec un long sel comme ça, vous êtes bien assis (même si une ponctuation/des chiffres aléatoires pourraient l'améliorer).

Vous pouvez également utiliser SHA-1, qui semble être de plus en plus populaire ces derniers temps.

6 votes

La note en bas de l'article de M. Atwood (en rouge) renvoie à un autre article d'un spécialiste de la sécurité qui affirme que l'utilisation de MD5, SHA1 et autres hachages rapides pour stocker les mots de passe est une erreur.

1 votes

Il n'est mauvais que dans la mesure où les utilisateurs peuvent le forcer brutalement plus rapidement. Les hachages "lents" sont généralement rendus lents en introduisant une attente. Il s'agit d'une fausse sécurité. Il est préférable de limiter le nombre de tentatives de connexion d'une personne.

2 votes

@Matthew Scharley : Je ne suis pas d'accord pour dire que l'effort supplémentaire imposé par les algorithmes de hachage de mots de passe coûteux est une fausse sécurité. C'est pour se prémunir contre le brute-forcing de mots de passe facilement devinables. Si vous limitez les tentatives de connexion, vous vous protégez contre la même chose (bien que de manière un peu plus efficace). Mais si un adversaire a accès aux hachages stockés dans la base de données, il sera en mesure de forcer brutalement ces mots de passe (faciles à deviner) assez rapidement (en fonction de la facilité avec laquelle ils sont devinés). La valeur par défaut de l'algorithme de cryptage SHA-256 est de 10000 tours, ce qui rendrait la tâche 10000 fois plus difficile.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X