web-dev-qa-db-fra.com

Chiffrement / hachage des mots de passe en texte brut dans la base de données

J'ai hérité une application web que je viens de découvrir stocke plus de 300 000 noms d'utilisateur/mots de passe en texte brut dans une base de données SQL Server. Je me rends compte que c'est une Very Bad Thing ™.

Sachant que je devrai mettre à jour les processus de mise à jour des identifiants et des mots de passe pour crypter/décrypter, et avec le moindre impact sur le reste du système, quelle serait la meilleure façon de supprimer les mots de passe en texte brut de la base de données?

Toute aide est appréciée.

Edit: Désolé si je ne suis pas clair, je voulais vous demander quelle serait votre procédure pour crypter/hacher les mots de passe, pas des méthodes de cryptage/hachage spécifiques.

Dois-je simplement:

  1. Faire une sauvegarde de la base de données
  2. Mettre à jour le code de connexion/mise à jour du mot de passe
  3. Après les heures, parcourez tous les enregistrements de la table des utilisateurs en hachant le mot de passe et en remplaçant chacun
  4. Testez pour vous assurer que les utilisateurs peuvent toujours se connecter/mettre à jour les mots de passe

Je suppose que ma préoccupation vient davantage du nombre d'utilisateurs, donc je veux m'assurer que je fais cela correctement.

62
Jonathan S.

J'imagine que vous devrez ajouter une colonne à la base de données pour le mot de passe crypté puis exécuter un travail par lots sur tous les enregistrements qui obtient le mot de passe actuel, le crypte (comme d'autres l'ont mentionné, un hachage comme md5 est assez standard modifier : mais ne doit pas être utilisé seul - voir les autres réponses pour de bonnes discussions), le stocke dans la nouvelle colonne et vérifie que tout s'est bien passé.

Ensuite, vous devrez mettre à jour votre frontal pour hacher le mot de passe entré par l'utilisateur au moment de la connexion et vérifier que par rapport au hachage stocké, plutôt que de vérifier le texte en clair contre le texte en clair.

Il me semblerait prudent de laisser les deux colonnes en place pendant un petit moment pour s'assurer que rien de bizarre ne s'est passé, avant de supprimer finalement les mots de passe en clair.

N'oubliez pas également qu'à chaque fois que le mot de passe est accédé, le code devra changer, comme les demandes de changement/rappel de mot de passe. Vous perdrez bien sûr la possibilité d'envoyer par e-mail des mots de passe oubliés, mais ce n'est pas une mauvaise chose. Vous devrez utiliser un système de réinitialisation de mot de passe à la place.

Edit: Un dernier point, vous voudrez peut-être envisager d'éviter l'erreur que j'ai faite lors de ma première tentative sur un site Web de connexion sécurisée au banc d'essai:

Lors du traitement du mot de passe utilisateur, déterminez où le hachage a lieu. Dans mon cas, le hachage a été calculé par le code PHP exécuté sur le serveur Web, mais le mot de passe a été transmis à la page de la machine de l'utilisateur en texte clair! C'était ok (ish) dans l'environnement I fonctionnait, car il était de toute façon dans un système https (réseau uni). Mais, dans le monde réel, j'imagine que vous voudriez hacher le mot de passe avant qu'il ne quitte le système utilisateur, en utilisant javascript, etc., puis transmettre le hachage à votre site.

19
xan

EDIT (2016): utilisez Argon2 , scrypt , bcrypt , ou PBKDF2 , dans cet ordre de préférence. Utilisez un facteur de ralentissement aussi important que possible pour votre situation. Utilisez une implémentation existante approuvée. Assurez-vous que vous utilisez un sel approprié (bien que les bibliothèques que vous utilisez devraient s'en assurer pour vous).


Lorsque vous hachez les mots de passe, utilisez N'UTILISEZ PAS PLAIN MD5 .

Utilisez PBKDF2 , ce qui signifie essentiellement utiliser un sel aléatoire pour empêcher Table arc-en-ciel les attaques, et répéter (re-hacher) suffisamment de fois pour ralentir le hachage - pas tellement que votre l'application prend trop de temps, mais suffisamment pour qu'un attaquant forçant un grand nombre de mots de passe différents le remarque

Extrait du document:

  • Répétez au moins 1 000 fois, de préférence plus - chronométrez votre mise en œuvre pour voir combien d'itérations sont réalisables pour vous.
  • 8 octets (64 bits) de sel suffisent, et l'aléatoire n'a pas besoin d'être sécurisé (le sel n'est pas chiffré, nous n'avons pas peur que quelqu'un le devine).
  • Une bonne façon d'appliquer le sel lors du hachage est d'utiliser HMAC avec votre algorithme de hachage préféré, en utilisant le mot de passe comme clé HMAC et le sel comme texte à hacher (voir cette section du document).

Exemple d'implémentation en Python, utilisant SHA-256 comme hachage sécurisé:

[~ # ~] modifier [~ # ~] : comme mentionné par Eli Collins, ce n'est pas une implémentation PBKDF2. Vous devriez préférer les implémentations qui respectent la norme, telles que PassLib .

from hashlib import sha256
from hmac import HMAC
import random

def random_bytes(num_bytes):
  return "".join(chr(random.randrange(256)) for i in xrange(num_bytes))

def pbkdf_sha256(password, salt, iterations):
  result = password
  for i in xrange(iterations):
    result = HMAC(result, salt, sha256).digest() # use HMAC to apply the salt
  return result

NUM_ITERATIONS = 5000
def hash_password(plain_password):
  salt = random_bytes(8) # 64 bits

  hashed_password = pbkdf_sha256(plain_password, salt, NUM_ITERATIONS)

  # return the salt and hashed password, encoded in base64 and split with ","
  return salt.encode("base64").strip() + "," + hashed_password.encode("base64").strip()

def check_password(saved_password_entry, plain_password):
  salt, hashed_password = saved_password_entry.split(",")
  salt = salt.decode("base64")
  hashed_password = hashed_password.decode("base64")

  return hashed_password == pbkdf_sha256(plain_password, salt, NUM_ITERATIONS)

password_entry = hash_password("mysecret")
print password_entry # will print, for example: 8Y1ZO8Y1pi4=,r7Acg5iRiZ/x4QwFLhPMjASESxesoIcdJRSDkqWYfaA=
check_password(password_entry, "mysecret") # returns True
49
orip

La stratégie de base consiste à utiliser une fonction de dérivation de clé pour "hacher" le mot de passe avec du sel. Le sel et le résultat du hachage sont stockés dans la base de données. Lorsqu'un utilisateur entre un mot de passe, le sel et leur entrée sont hachés de la même manière et comparés à la valeur stockée. S'ils correspondent, l'utilisateur est authentifié.

Le diable est dans les détails. Tout d'abord, beaucoup dépend de l'algorithme de hachage choisi. Un algorithme de dérivation de clé comme PBKDF2, basé sur un code d'authentification de message basé sur le hachage, rend "impossible pour le calcul" de trouver une entrée (dans ce cas, un mot de passe) qui produira une sortie donnée (ce qu'un attaquant a trouvé dans la base de données ).

Une attaque par dictionnaire pré-calculé utilise un index ou un dictionnaire pré-calculé, des sorties de hachage aux mots de passe. Le hachage est lent (ou il est censé l'être de toute façon), donc l'attaquant hache tous les mots de passe probables une fois et stocke le résultat indexé de telle manière que, étant donné un hachage, il peut rechercher un mot de passe correspondant. Il s'agit d'un compromis classique entre l'espace et le temps. Étant donné que les listes de mots de passe peuvent être énormes, il existe des moyens d'ajuster le compromis (comme les tables Rainbow), de sorte qu'un attaquant peut abandonner un peu de vitesse pour économiser beaucoup d'espace.

Les attaques de pré-calcul sont contrecarrées en utilisant du "sel cryptographique". Il s'agit de certaines données hachées avec le mot de passe. Il n'a pas besoin d'être secret, il doit simplement être imprévisible pour un mot de passe donné. Pour chaque valeur de sel, un attaquant aurait besoin d'un nouveau dictionnaire. Si vous utilisez un octet de sel, un attaquant a besoin de 256 copies de son dictionnaire, chacune générée avec un sel différent. Tout d'abord, il utiliserait le sel pour rechercher le bon dictionnaire, puis il utiliserait la sortie de hachage pour rechercher un mot de passe utilisable. Mais que faire si vous ajoutez 4 octets? Maintenant, il a besoin de 4 milliards d'exemplaires du dictionnaire. En utilisant un sel suffisamment grand, une attaque par dictionnaire est exclue. En pratique, 8 à 16 octets de données provenant d'un générateur de nombres aléatoires de qualité cryptographique font un bon sel.

Avec le pré-calcul hors de la table, un attaquant a calculé le hachage à chaque tentative. Le temps qu'il faut pour trouver un mot de passe dépend maintenant entièrement du temps qu'il faut pour hacher un candidat. Ce temps est augmenté par l'itération de la fonction de hachage. Les itérations numériques sont généralement un paramètre de la fonction de dérivation de clé; aujourd'hui, de nombreux appareils mobiles utilisent 10 000 à 20 000 itérations, tandis qu'un serveur peut en utiliser 100 000 ou plus. (L'algorithme bcrypt utilise le terme "facteur de coût", qui est une mesure logarithmique du temps requis.)

38
erickson

Suivez les conseils de Xan de garder la colonne de mot de passe actuelle pendant un certain temps, donc si les choses tournent mal, vous pouvez revenir en arrière rapidement.

En ce qui concerne le cryptage de vos mots de passe:

  • utiliser un sel
  • utiliser un algorithme de hachage destiné aux mots de passe (c'est-à-dire, - c'est lent )

Voir Thomas Ptacek Assez avec les tables arc-en-ciel: ce que vous devez savoir sur les schémas de mots de passe sécurisés pour plus de détails.

4
Michael Burr

Je pense que vous devriez faire ce qui suit:

  1. Créez une nouvelle colonne appelée HASHED_PASSWORD ou quelque chose de similaire.
  2. Modifiez votre code afin qu'il vérifie les deux colonnes.
  3. Migrez progressivement les mots de passe de la table non hachée vers la table hachée. Par exemple, lorsqu'un utilisateur se connecte, migrez automatiquement son mot de passe vers la colonne hachée et supprimez la version non hachée. Tous les utilisateurs nouvellement enregistrés auront des mots de passe hachés.
  4. Après les heures, vous pouvez exécuter un script qui migre n utilisateurs à la fois
  5. Lorsque vous n'avez plus de mots de passe non hachés, vous pouvez supprimer votre ancienne colonne de mots de passe (vous ne pourrez peut-être pas le faire, cela dépend de la base de données que vous utilisez). En outre, vous pouvez supprimer le code pour gérer les anciens mots de passe.
  6. Vous avez terminé!
3
Tamas Czinege

C'était un de mes problèmes il y a quelques semaines. Nous déployions un grand projet SIG dans 975 emplacements géographiques différents où notre propre magasin de références d'utilisateur sera utilisé comme authentificateur pour différents ensembles d'applications déjà implémentées et en cours d'utilisation. Nous avons déjà fourni les deux REST et SOAP service d'authentification basé mais le client a insisté pour pouvoir accéder au magasin d'informations d'identification des utilisateurs à partir d'autres applications avec juste une connexion DB à lire) -seulement la vue ou la table associée. Soupir ... (cette mauvaise décision de conception fortement couplée fait l'objet d'une autre question).

Cela nous a obligés à nous asseoir et à convertir notre schéma de stockage de mot de passe salé et haché de manière itérative en spécification et à fournir différentes implémentations de langage pour une intégration facile.

Nous l'avons appelé Fairly Secure Hashed Passwords ou FSHP en bref. Mis en œuvre en Python, Ruby, PHP5 et publié sur le domaine public. Disponible pour être consommé, bifurqué, flammé ou craché sur GitHub à http://github.com/bdd/fshp

FSHP est une implémentation de hachage de mot de passe salée et hachée de manière itérative.

Le principe de conception est similaire avec la spécification PBKDF1 dans la RFC 2898 (alias: PKCS # 5: Spécification de cryptographie basée sur mot de passe version 2.0.) FSHP permet de choisir la longueur du sel, le nombre d'itérations et de la fonction de hachage cryptographique sous-jacente parmi SHA-1 et SHA-2 (256, 384, 512). Le préfixe méta auto-défini au début de chaque sortie le rend portable tout en laissant le consommateur choisir sa propre ligne de base de sécurité de stockage de mot de passe.

[~ # ~] sécurité [~ # ~] :

FSHP1 par défaut utilise des sels de 8 octets, avec 4096 itérations de hachage SHA-256. - Le sel de 8 octets rend les attaques de table Rainbow impraticables en multipliant l'espace requis par 2 ^ 64. - 4096 itérations font que les attaques par force brute sont assez chères. - Il n'y a aucune attaque connue contre SHA-256 pour trouver des collisions avec un effort de calcul de moins de 2 ^ 128 opérations au moment de cette version.

MISES EN ŒUVRE:

  • Python: testé avec 2.3.5 (avec hashlib), 2.5.1, 2.6.1
  • Ruby: testé avec 1.8.6
  • PHP5: testé avec 5.2.6

Tout le monde est plus que bienvenu pour créer des implémentations de langage manquantes ou peaufiner les implémentations actuelles.

OPÉRATION DE BASE (avec Python):

>>> fsh = fshp.crypt('OrpheanBeholderScryDoubt')
>>> print fsh
{FSHP1|8|4096}GVSUFDAjdh0vBosn1GUhzGLHP7BmkbCZVH/3TQqGIjADXpc+6NCg3g==
>>> fshp.validate('OrpheanBeholderScryDoubt', fsh)
True

PERSONNALISATION DE LA CRYPTE:

Affaiblissons notre schéma de hachage de mot de passe. - Diminuez la longueur de sel de 8 à 2 par défaut. - Diminuez le cycle d'itération de 4096 à 10 par défaut. - Sélectionnez FSHP0 avec SHA-1 comme algorithme de hachage sous-jacent.

>>> fsh = fshp.crypt('ExecuteOrder66', saltlen=2, rounds=10, variant=0)
>>> print fsh
{FSHP0|2|10}Nge7yRT/vueEGVFPIxcDjiaHQGFQaQ==
3
Berk D. Demir

Comme les autres l'ont mentionné, vous ne voulez pas décrypter si vous pouvez l'aider. La meilleure pratique standard consiste à chiffrer à l'aide d'un hachage unidirectionnel, puis lorsque l'utilisateur se connecte à hacher son mot de passe pour le comparer.

Sinon, vous devrez utiliser un cryptage fort pour crypter puis décrypter. Je ne recommanderais cela que si les raisons politiques sont fortes (par exemple, vos utilisateurs sont habitués à pouvoir appeler le service d'assistance pour récupérer leur mot de passe, et vous avez une forte pression du haut pour ne pas changer cela). Dans ce cas, je commencerais par le chiffrement, puis commencerais à créer une analyse de rentabilisation pour passer au hachage.

2
Cory Foy

À des fins d'authentification, vous devez éviter de stocker les mots de passe à l'aide d'un cryptage réversible, c'est-à-dire que vous devez uniquement stocker le hachage du mot de passe et comparer le hachage du mot de passe fourni par l'utilisateur avec le hachage que vous avez stocké. Cependant, cette approche a un inconvénient: elle est vulnérable aux attaques Rainbow table , si un attaquant se saisissait de votre base de données du magasin de mots de passe.

Ce que vous devez faire est de stocker les hachages d'une valeur de sel pré-choisie (et secrète) + le mot de passe. C'est-à-dire, concaténer le sel et le mot de passe, hacher le résultat et stocker ce hachage. Lors de l'authentification, faites de même - concaténez votre valeur de sel et le mot de passe fourni par l'utilisateur, hachage, puis vérifiez l'égalité. Cela rend les attaques Rainbow table irréalisables.

Bien sûr, si l'utilisateur envoie des mots de passe sur le réseau (par exemple, si vous travaillez sur une application Web ou client-serveur), vous ne devez pas envoyer le mot de passe en texte clair, donc au lieu de stocker du hachage (sel + mot de passe), vous devez stocker et vérifier le hachage (sel + hachage (mot de passe)), et demander à votre client de pré-hacher le mot de passe fourni par l'utilisateur et de l'envoyer sur le réseau. Cela protège également le mot de passe de votre utilisateur, si l'utilisateur (comme beaucoup le font) réutilise le même mot de passe à des fins multiples.

2
Mihai Limbășan
  • Crypter en utilisant quelque chose comme MD5, le coder comme une chaîne hexadécimale
  • Vous avez besoin d'un sel; dans votre cas, le nom d'utilisateur peut être utilisé comme sel (il doit être unique, le nom d'utilisateur doit être la valeur la plus unique disponible ;-)
  • utilisez l'ancien champ de mot de passe pour stocker le MD5, mais étiquetez le MD5 (par exemple, "MD5: 687A878 ....") afin que les anciens (texte brut) et nouveaux (MD5) mots de passe puissent coexister
  • changer la procédure de connexion pour vérifier par rapport au MD5 s'il y a un MD5, et par rapport au mot de passe ordinaire sinon
  • changer les fonctions "changer le mot de passe" et "nouvel utilisateur" pour créer des mots de passe MD5 seulement
  • vous pouvez maintenant exécuter le travail de conversion par lots, ce qui peut prendre aussi longtemps que nécessaire
  • une fois la conversion exécutée, supprimez le support hérité
1
mfx

Étape 1: ajouter un champ crypté à la base de données

Étape 2: modifiez le code de sorte que lorsque le mot de passe est modifié, il met à jour les deux champs, mais la connexion utilise toujours l'ancien champ.

Étape 3: exécutez le script pour remplir tous les nouveaux champs.

Étape 4: modifiez le code afin que la connexion utilise un nouveau champ et que le changement de mot de passe arrête la mise à jour de l'ancien champ.

Étape 5: supprimez les mots de passe non chiffrés de la base de données.

Cela devrait vous permettre d'effectuer le basculement sans interruption pour l'utilisateur final.

Aussi: Quelque chose que je ferais serait de nommer le nouveau champ de base de données quelque chose qui n'a rien à voir avec le mot de passe comme "LastSessionID" ou quelque chose de même ennuyeux. Ensuite, au lieu de supprimer le champ de mot de passe, remplissez simplement avec des hachages de données aléatoires. Ensuite, si votre base de données est compromise, ils peuvent passer tout le temps qu'ils souhaitent à décrypter le champ "mot de passe".

Cela n'accomplit peut-être rien, mais c'est amusant de penser à quelqu'un assis là essayant de trouver des informations sans valeur

1
Kevin

Comme pour toutes les décisions de sécurité, il y a des compromis. Si vous hachez le mot de passe, ce qui est probablement votre démarche la plus simple, vous ne pouvez pas offrir une fonction de récupération de mot de passe qui renvoie le mot de passe d'origine, ni votre personnel ne peut rechercher le mot de passe d'une personne afin d'accéder à son compte.

Vous pouvez utiliser un cryptage symétrique, qui a ses propres inconvénients de sécurité. (Si votre serveur est compromis, la clé de chiffrement symétrique peut également être compromise).

Vous pouvez utiliser le chiffrement à clé publique et exécuter la récupération de mot de passe/le service client sur une machine distincte qui stocke la clé privée indépendamment de l'application Web. C'est la plus sécurisée, mais elle nécessite une architecture à deux machines et probablement un pare-feu entre les deux.

0
davidcl

Je voudrais suggérer une amélioration à le grand exemple python publié par Orip . Je redéfinirais la fonction random_bytes Pour être:

def random_bytes(num_bytes):
    return os.urandom(num_bytes)

Bien sûr, vous devrez importer le module os. La fonction os.urandom Fournit une séquence aléatoire d'octets qui peut être utilisée en toute sécurité dans des applications cryptographiques. Voir l'aide de référence de cette fonction pour plus de détails.

0
Sergio

MD5 et SHA1 ont montré un peu de faiblesse (deux mots peuvent entraîner le même hachage), donc l'utilisation de SHA256-SHA512/hachages itératifs est recommandée pour hacher le mot de passe.

J'écrirais un petit programme dans la langue dans laquelle l'application est écrite et qui génère un sel aléatoire unique pour chaque utilisateur et un hachage du mot de passe. La raison pour laquelle j'ai tendance à utiliser le même langage que la vérification est que différentes bibliothèques de chiffrement peuvent faire les choses légèrement différemment (c'est-à-dire le remplissage), donc utiliser la même bibliothèque pour générer le hachage et vérifier qu'il élimine ce risque. Cette application pourrait également vérifier la connexion après la mise à jour de la table si vous le souhaitez car elle connaît toujours le mot de passe en texte brut.

  1. N'utilisez pas MD5/SHA1
  2. Générez un bon sel aléatoire (de nombreuses bibliothèques de crypto ont un générateur de sel)
  3. Un algorithme de hachage itératif recommandé par orip
  4. Assurez-vous que les mots de passe ne sont pas transmis en texte brut sur le fil
0
Kudos2u2

Je ne suis pas un expert en sécurité, mais je pense que la recommandation actuelle est d'utiliser bcrypt/blowfish ou une variante SHA-2, pas MD5/SHA1.

Vous devez probablement aussi penser à un audit de sécurité complet

0
Gene T