Tout d'abord, mon motif est d'éviter de stocker le sel dans la base de données sous forme de texte brut. En ce qui concerne cette question, le sel n'est pas stocké dans la base de données.
Après discussion dans les commentaires et dans le chat , j'ai trouvé une théorie:
Il semble que l'utilisation de doman_name + user ID
à côté d'un poivre fournira une combinaison suffisante de caractère aléatoire et d'unicité.
Une méthode comme celle-ci fournirait-elle autant de sécurité qu'un sel aléatoire sans avoir à stocker un sel désigné dans la base de données sous forme de texte brut?
La seule exigence d'un sel est d'être globalement unique. Il n'est pas nécessaire d'être aléatoire et il ne doit certainement pas être inconnu.
Notez le mot-clé globalement . Un sel doit être unique non seulement dans le contexte de votre base de données, mais dans le contexte de chaque base de données. Un e-mail ou un ID utilisateur incrémentiel est (très) peu susceptible de répondre à cette exigence. L'utilisation d'un sel généré de manière aléatoire le fait.
MODIFIER
Je vais aborder le tout en utilisant un domain + userid
comme une idée de sel qui est apparue dans les commentaires. Je ne pense pas que ce schéma soit une bonne idée, surtout par rapport à l'utilisation d'un sel aléatoire.
Si votre site est une cible élevée, un attaquant pourrait trouver utile de générer une table Rainbow ciblant le compte administrateur avant que l'attaque ne se produise. Cela pourrait lui permettre d'accéder avant que l'attaque ne soit découverte. Le domain + userid
le schéma ne fonctionne pas correctement lorsque vous envisagez cette situation car le compte administrateur se trouve généralement dans les premiers userid
du système, en particulier lorsque userid
est incrémenté à l'aide d'un compteur.
Cela étant dit, toute cette discussion est vraiment théorique dans la pratique. La génération de sels aléatoires est simple et rapide. De nombreuses bibliothèques de hachage de mot de passe, en particulier celles bcrypt
le font déjà pour vous. Il ne sert à rien de faire rouler votre propre schéma même s'il est tout aussi sécurisé. Avouons-le, nous sommes tous des gens paresseux, n'est-ce pas? : P Pourquoi réinventer la roue?
La caractéristique importante d'un sel de mot de passe ( comme Terry Chia le note correctement ) est qu'il devrait être unique au monde: aucun hachage de mot de passe (même sur des sites différents) ne devrait jamais avoir le même sel.
L'utilisation de sels uniques protège contre diverses méthodes d'attaque associées qui pourraient être utilisées par un attaquant qui a compromis la base de données des utilisateurs et souhaite récupérer les mots de passe de leurs hachages:
Tout d'abord, cela empêche un attaquant de dire facilement si plusieurs utilisateurs ont le même mot de passe (ce qui rendrait ces utilisateurs une cible évidente, car un tel mot de passe commun serait probablement facile à deviner et fournirait un gain élevé).
Inversement, cela empêche également un attaquant qui a compromis plusieurs sites de pouvoir facilement dire quels utilisateurs ont utilisé le même mot de passe sur les deux sites.
De plus, l'utilisation d'un sel unique pour chaque utilisateur empêche un attaquant d'obtenir any avantage d'attaquer plusieurs comptes en même temps. Sans sels (ou avec des sels non uniques), un attaquant pourrait hacher un mot de passe deviné, puis le comparer à chaque hachage de mot de passe dans la base de données, ce qui rend plus probable qu'ils obtiennent au moins une correspondance. Avec des sels uniques, cela est impossible, car l'attaquant doit choisir le sel (et donc un seul utilisateur cible) avant de hacher chaque mot de passe.
Enfin, des sels uniques ne permettent pas non plus d'utiliser des tables de recherche de hachage précalculées (comme les "tables arc-en-ciel") pour accélérer le craquage des mots de passe, car, essentiellement, une table distincte serait nécessaire pour chaque utilisateur.
Fondamentalement, le principal objectif du salage est de garantir qu'un crackage de plusieurs mots de passe compromis ne soit pas plus facile que le crackage de chacun d'eux séparément - rien de plus et rien de moins. L'utilisation de sels uniques au monde permet d'atteindre cet objectif.
Un moyen facile de garantir l'unicité globale (avec une forte probabilité) est d'utiliser une chaîne aléatoire suffisamment longue comme sel. Cependant, d'autres moyens existent également.
Par exemple, vous pouvez obtenir un sel unique au niveau mondial en combinant un identifiant d'utilisateur localement unique (comme une adresse e-mail ou un numéro de compte) avec un identifiant de site unique et globalement fixe (par exemple le nom de domaine d'un site Web), et éventuellement certains autres éléments comme un indicateur de finalité (au cas où vous pourriez avoir besoin de plus d'un sel unique par utilisateur) et un horodatage (de sorte qu'un nouveau mot de passe obtiendra toujours un sel différent). Par exemple, la chaîne suivante serait un bon sel unique au monde:
"Il s'agit d'un sel de mot de passe pour l'utilisateur 5457 sur security.stackexchange.com créé le 1er septembre 2013 13: 35: 23.94730 UTC"
et celui-ci aussi:
"password_salt: 5457: security.stackexchange.com: 20130901T133523.94730"
Si vous préférez que vos sels soient plus compacts, vous pouvez également alimenter l'une des chaînes ci-dessus via une fonction de hachage cryptographique comme SHA-256 et utiliser la sortie comme sel. Ou, si votre système fournit un générateur GUID intégré, vous pouvez simplement l'utiliser (en supposant qu'il fonctionne comme il se doit, bien sûr).
Cela dit, il y a c'est un avantage potentiel à rendre les sels de mot de passe non seulement uniques mais aussi imprévisibles: cela empêche l'attaquant de lancer une attaque par force brute avant ils ont compromis votre base de données.
Fondamentalement, si moi, en tant qu'attaquant, je savais que le sel de votre compte administrateur était, par exemple, "12345678", alors je pourrais configurer mes ordinateurs pour compiler une table de recherche de mots de passe plus ou moins courants hachés avec ce sel même avant d'avoir trouvé un moyen d'obtenir votre hachage de mot de passe réel. Une fois que j'ai trouvé un moyen de le faire (peut-être des semaines ou des mois plus tard), je pouvais rechercher le hachage dans ma table, en espérant qu'il pourrait correspondre à l'un des mots de passe que mon programme avait déjà essayés entre-temps, auquel cas je connaîtrait immédiatement le mot de passe.
Cependant, si je ne savais même pas quel était le sel que vous avez utilisé, je ne serais pas en mesure de deviner le mot de passe avant d'avoir obtenu le bon sel. Cela prendrait plus de temps, vous permettant potentiellement de changer le mot de passe avant que je ne parvienne à le déchiffrer.
Encore une fois, les longs sels aléatoires sont (avec une probabilité élevée) non seulement uniques mais aussi imprévisibles. Cela dit, il existe également d'autres moyens d'atteindre cet objectif. Par exemple, vous pouvez prendre l'un des exemples de sels uniques indiqués ci-dessus et y ajouter simplement une valeur secrète (par exemple, une chaîne aléatoire stockée dans un fichier de configuration, éventuellement modifiée régulièrement). Étant donné que ce secret serait le même pour tous les utilisateurs, cela ne contribuerait pas à l'unicité, mais cela garantirait que je ne serais pas en mesure de lancer une attaque par force brute sans d'abord apprendre le secret.
Addendum: En ce qui concerne vos modifications à la question, je dirais que votre schéma (en utilisant le nom de domaine + l'ID utilisateur + le poivre comme sel) devrait être suffisant . Si possible, j'inclurais également un horodatage, mais cela nécessite évidemment que vous stockiez l'horodatage quelque part.
Cela dit, je trouve toujours votre prémisse ("le sel n'est pas stocké dans la base de données") un peu déroutante. Si vous pouvez stocker le mot de passe haché lui-même dans la base de données, pourquoi pas le sel aussi?
En fait, de nombreux schémas de hachage de mot de passe courants n'utilisent pas du tout une colonne de base de données distincte pour le sel, ils la stockent simplement dans le cadre de la chaîne de hachage, par exemple comme method$salt$hash
, où method
identifie la méthode de hachage utilisée, salt
et hash
sont les valeurs de sel et de hachage codées en utilisant un schéma approprié (par exemple base64) et $
est juste un caractère de séparation arbitraire qui ne se produit pas dans le sel ou le hachage codé.
Un sel est censé être unique, doit être suffisamment long et doit être imprévisible. L'aléatoire n'est pas nécessaire, mais c'est le moyen le plus simple pour un ordinateur de répondre à ces exigences. Et ce n'est pas le but d'un sel d'être secret, un sel remplit son objectif même lorsqu'il est connu.
nicité signifie qu'il devrait non seulement être unique dans votre base de données (sinon vous pourriez utiliser un ID utilisateur), il devrait être unique dans le monde entier. Quelqu'un pourrait créer des tables arc-en-ciel pour des sels comme par exemple 1-1000 et serait en mesure de récupérer les mots de passe pour tous les comptes avec ces ID utilisateur (souvent les comptes administrateur ont des ID utilisateur faibles).
Assez long: Si le sel est trop court (trop peu de combinaisons possibles), il devient à nouveau rentable de construire des Rainbow-tables. Le sel et le mot de passe peuvent alors être considérés comme un simple mot de passe plus long, et si vous pouvez créer une table arc-en-ciel pour ces mots de passe plus longs, vous obtenez également les mots de passe originaux plus courts. Pour les mots de passe très forts et longs, le salage ne serait en fait pas nécessaire du tout, mais la plupart des mots de passe générés par l'homme peuvent être forcés par la force parce qu'ils sont courts (les gens doivent s'en souvenir).
L'utilisation de sels dérivés d'autres paramètres peut également entrer dans cette catégorie. Ce n'est que parce que vous calculez un hachage à partir de l'ID utilisateur que cela n'augmente pas les combinaisons possibles.
npredictability est un peu moins important, mais imaginez une fois de plus le cas où vous utilisez l'ID utilisateur comme sel, un attaquant peut découvrir ce que seront les prochains ID utilisateur, et peut donc précalculer un nombre restreint de Rainbow -les tables. Selon l'algorithme de hachage utilisé, cela peut être applicable ou non. Il a alors un avantage de temps, peut récupérer le mot de passe immédiatement. Le problème sera plus important si les comptes d'administrateur utilisent un sel prévisible.
Donc, utiliser un nombre vraiment aléatoire, généré à partir de la source aléatoire du système d'exploitation (dev/urandom), est le meilleur que vous puissiez faire. Même si vous ignorez toutes les raisons ci-dessus, pourquoi devriez-vous utiliser un sel dérivé quand il existe une meilleure façon, pourquoi ne pas utiliser la meilleure façon que vous connaissez?
PDATE: pour répondre à la question modifiée:
Utiliser uniquement domain_name + user_id pour générer le sel n'est certainement pas une bonne idée, car il peut être deviné et le rend prévisible. Un poivre n'aidera pas ici, car il est aussi constant que le nom_domaine, vous ne devez pas vous y fier pour être secret. Avec le user_id vous gagnez l'unicité, avec le domain_name vous obtenez l'unicité globale, mais il vous manque la partie imprévisible.
Dans votre exemple précédent, vous avez ajouté le mot de passe lui-même en tant que paramètre aléatoire comme salt = hash(domain_name + user_id + password)
. Cela pourrait fonctionner tant que le sel ne sera pas stocké du tout, pour vérifier le mot de passe, vous auriez tous les paramètres nécessaires. Je ne vois pas de défaut dans ce concept, mais cela ne signifie pas que je le recommande. Comme déjà mentionné, vous ne pouvez pas faire mieux que de générer un sel indépendant aléatoire. Ajouter ce sel au hachage et l'extraire pour vérification n'est pas une tâche difficile, les fonctions de hachage les plus appropriées le feront déjà pour vous, car elles doivent également stocker le facteur de coût de la même manière.
Tout d'abord, mon motif est d'éviter de stocker le sel dans la base de données sous forme de texte brut.
Pourquoi? Il n'y a aucune bonne raison d'éviter de stocker le sel dans la base de données en texte brut. C'est ainsi qu'un sel est normalement stocké.
Avec les différentes itérations de votre question, vous avez proposé différentes façons de faire les choses différemment, mais elles finissent toutes par avoir les mêmes propriétés de sécurité ou (généralement) aggravent les choses.
Le but d'un sel est d'empêcher les attaquants de partager le travail lors du crack de nombreux comptes à la fois. Souvent, les attaquants ne recherchent pas un utilisateur particulier sur un site particulier, ce qu'ils veulent, c'est prendre pied ou casser autant de comptes que possible. Le but du sel est de faire en sorte que le crack du compte de Joe sur le site ACME nécessite des calculs complètement différents du crack du compte de Jane ou du compte de Joe sur le site Yoyodyne. D'où la nécessité d'un sel unique à l'échelle mondiale: entre comptes et entre bases de données (et même dans le temps).
Si vous utilisez le mot de passe dans le cadre du calcul du sel, ce n'est pas du tout un sel. Le sel doit être indépendant du mot de passe. Si vous calculez le sel à partir du mot de passe, il fait alors partie de la fonction de hachage: H (S (P), P) est une fonction de P, vous n'avez créé qu'une nouvelle fonction de hachage H '(P) = H ( S (P), P).
L'utilisation du nom d'utilisateur n'est pas bonne car elle peut être la même dans toutes les bases de données. Un attaquant disposant de la base de données ACME et de la base de données Yoyodyne peut casser les deux bases de données sans dupliquer l'effort s'il utilise du sel dérivé du nom d'utilisateur.
Si vous utilisez le nom de domaine et l'ID utilisateur, cela peut fonctionner. Assurez-vous que personne d'autre au monde n'utilise le même nom de domaine; en particulier, n'utilisez pas le même nom de domaine si vous avez plusieurs sous-sites: incluez le nom du service ou tout ce qu'il faut pour rendre le nom unique. De même, ne réutilisez pas les ID utilisateur. Tant que votre sel est unique au monde, c'est un vrai sel.
Notez cependant que vous n'obtiendrez aucune sécurité en utilisant le nom de domaine et l'ID utilisateur comme sel plutôt qu'un sel aléatoire comme cela est généralement fait. Le sel est toujours efficacement stocké dans la base de données, juste d'une manière étrange. Vous enregistrez quelques octets par enregistrement de base de données, ce qui ne devrait pas être significatif.
Un poivre n'améliore pas non plus de façon significative la sécurité. Vous ne pouvez pas supposer que votre poivre reste secret. Si l'attaquant a pu obtenir une copie de votre base de données de mots de passe (généralement en compromettant votre application ou en accédant à une sauvegarde), il est probable qu'ils aient également accès au poivre (il doit être accessible à l'application et sauvegardé après tout). Le seul cas où un poivre est utile est un compromis tel que l'injection SQL qui n'accorde à l'attaquant qu'un accès à la base de données et ne s'étend pas à un compromis d'application, pas même via un compte administratif. Ne comptez pas là-dessus. Même si vous utilisez un poivre, votre sel doit être globalement unique .
Je recommande fortement de générer un sel aléatoire et de le stocker à côté du mot de passe. De préférence, ne codez pas vous-même: utilisez une bibliothèque existante qui le fait correctement . Plus vous vous écartez de la pratique recommandée, plus vous risquez de le faire mal - peut-être complètement, dangereusement mal comme dériver le sel du mot de passe.
N'oubliez pas qu'en plus d'être salés, les hachages de mot de passe doivent être lents. Cela signifie PBKDF2 ou bcrypt ou scrypt, avec un nombre d'itérations ajusté aux vitesses actuelles de l'ordinateur.
Pour plus d'informations, lisez nos nombreuses discussions sur ce sujet:
Puisqu'aucune des autres réponses ne semble y faire allusion:
N'UTILISEZ PAS une partie du mot de passe comme sel. En plus de diminuer l'imprévisibilité, cela aidera, lorsqu'un attaquant le comprendra, un attaquant à casser les mots de passe. Pourquoi? Parce qu'ils en ont une partie juste à côté du mot de passe dans la base de données.
Mise à jour: Si vous utilisez une partie du mot de passe dans le sel et que vous voulez éviter d'avoir à stocker le sel, tirer une partie du mot de passe après sa saisie pour calculer le sel fonctionnera bien. Il complique davantage le forçage brut, et il est tout aussi efficace qu'un sel ordinaire pour augmenter l'entropie. Cela rend les tableaux Rainbow un peu plus difficiles à faire, car vous devez calculer le sel par mot de passe, mais cela ne les empêche pas vraiment mieux qu'un sel ordinaire.
En plus de la réponse exceptionnelle de @ terry-chia, il existe une autre raison de choisir des sels aléatoires. Le schéma de hachage et d'authentification que vous utilisez aujourd'hui peut imposer des exigences relativement faibles sur le salage, mais un jour vous pourriez (ou un successeur) modifier le schéma utilisé d'une manière qui a une exigence de caractère aléatoire.
Ainsi, bien que le caractère aléatoire ne soit pas une exigence, vous êtes beaucoup plus en sécurité si vous choisissez des sels aléatoires, car cela garantira que vous obtiendrez toutes les choses requises pour votre régime aujourd'hui, mais cela vous fournira également ce dont vous pourriez avoir besoin demain.
Encore une fois, une raison supplémentaire en plus de ce que d'autres ont déjà expliqué. Ne dérivez pas le sel des données utilisateur! L'utilisation d'un sel aléatoire peut vous apporter plus que ce dont vous avez besoin, mais cela vous évitera de faire d'autres erreurs. Vous n'avez rien à gagner à ne pas utiliser un sel aléatoire (à moins qu'il y ait quelque chose que vous n'avez pas encore expliqué), alors que vous rendez votre système beaucoup moins robuste en allant simplement pour les exigences minimales d'un sel.