web-dev-qa-db-fra.com

Comment générer un bon sel - Ma fonction est-elle suffisamment sécurisée?

Voici la fonction que j'utilise pour générer des sels aléatoires:

function generateRandomString($nbLetters){
    $randString="";
    $charUniverse="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    for($i=0; $i<$nbLetters; $i++){
       $randInt=Rand(0,61);
        $randChar=$charUniverse[$randInt];
        $randString=$randomString.$randChar;
    }
    return $randomString;
}

Il s'agit d'un site Web non commercial. Il est uniquement utilisé pour générer le sel (pour être stocké dans la base de données et utilisé avec le pw soumis par l'utilisateur pour le hachage).

Est-ce approprié? Dois-je utiliser un plus grand sous-ensemble de caractères, et si oui, existe-t-il un moyen simple de le faire en PHP?

37
JDelage

Si vous hachez des mots de passe, vous devez utiliser un algorithme de hachage moderne qui ne vous oblige pas à générer votre propre sel. L'utilisation d'algorithmes de hachage faibles présente un danger pour vous et vos utilisateurs. Ma réponse originale a été écrite il y a huit ans. Les temps ont changé et le hachage de mot de passe est désormais beaucoup plus facile.

Vous devez toujours utiliser les fonctions intégrées pour hacher/vérifier les mots de passe. L'utilisation de vos propres algorithmes à tout moment introduit une énorme quantité de risques inutiles.

Pour PHP, pensez à utiliser password_hash () , avec le PASSWORD_BCRYPT algorithme. Il n'est pas nécessaire de fournir votre propre sel.

Voici ma réponse originale, pour la postérité:


Avertissement : L'implémentation suivante ne produit pas de sel imprévisible, selon la documentation de niqid .

Depuis la page php sha1 :

$salt = uniqid(mt_Rand(), true);

Cela semble plus simple et plus efficace (car chacun est unique) que ce que vous avez proposé.

41
fredley

Si vous êtes sous Linux, /dev/urandom Est probablement votre meilleure source d'aléatoire. Il est fourni par le système d'exploitation lui-même, il est donc garanti d'être beaucoup plus fiable que n'importe quelle fonction intégrée PHP.

$fp = fopen('/dev/urandom', 'r');
$randomString = fread($fp, 32);
fclose($fp);

Cela vous donnera 32 octets de blob aléatoire. Vous voudrez probablement passer par quelque chose comme base64_encode() pour le rendre lisible. Pas besoin de jongler avec les personnages.

Edit 2014: Dans PHP 5.3 et supérieur, openssl_random_pseudo_bytes() est le moyen le plus simple d'obtenir un tas d'octets aléatoires. Sur les systèmes * nix, il utilise /dev/urandom en arrière-plan. Sur les systèmes Windows, il utilise un algorithme différent qui est intégré à la bibliothèque OpenSSL.

Connexes: https://security.stackexchange.com/questions/26206

En relation: dois-je utiliser urandom ou openssl_random_pseudo_bytes?

18
kijin

password_hash() est disponible en PHP 5.5 et plus récent. Je suis surpris d'apprendre qu'il n'est pas mentionné ici.

Avec password_hash (), il n'est pas nécessaire de générer un sel car le sel est automatiquement généré à l'aide de l'algorithme bcrypt - et donc pas besoin de constituer un ensemble de caractères.

Au lieu de cela, le mot de passe soumis par l'utilisateur est comparé au hachage de mot de passe unique stocké dans la base de données à l'aide de password_verify (). Il suffit de stocker le hachage de nom d'utilisateur et de mot de passe dans la table de la base de données utilisateur, vous pourrez ensuite le comparer à un mot de passe soumis par l'utilisateur à l'aide de password_verify ().

Comment fonctionne le hachage de mot de passe ():

La fonction password_hash () génère un hachage de mot de passe unique, lors du stockage de la chaîne dans une base de données - il est recommandé que la colonne autorise jusqu'à 255 caractères.

$password = "goat";
echo password_hash($password, PASSWORD_DEFAULT);
echo password_hash($password, PASSWORD_DEFAULT);
echo password_hash($password, PASSWORD_DEFAULT);

// Output example (store this in the database)
$2y$10$GBIQaf6gEeU9im8RTKhIgOZ5q5haDA.A5GzocSr5CR.sU8OUsCUwq  <- This hash changes.
$2y$10$7.y.lLyEHKfpxTRnT4HmweDKWojTLo1Ra0hXXlAC4ra1pfneAbj0K
$2y$10$5m8sFNEpJLBfMt/3A0BI5uH4CKep2hiNI1/BnDIG0PpLXpQzIHG8y 

Pour vérifier un mot de passe haché, vous utilisez password_verify() :

$password_enc = password_hash("goat", PASSWORD_DEFAULT);
dump(password_verify('goat', $password_enc)); // TRUE
dump(password_verify('fish', $password_enc)); // FALSE

Si vous préférez, le sel peut être ajouté manuellement en option, comme ceci:

$password = 'MyPassword';
$salt = 'MySaltThatUsesALongAndImpossibleToRememberSentence+NumbersSuch@7913';
$hash = password_hash($password, PASSWORD_DEFAULT, ['salt'=>$salt]);
// Output: $2y$10$TXlTYWx0VGhhdFVzZXNBT.ApoIjIiwyhEvKC9Ok5qzVcSal7T8CTu  <- This password hash not change.
8
Kristoffer Bohmann

Je prendrais les conseils d'une autre réponse et utiliserais mt_Rand (0, 61) , parce que le Twister de Mersenne produit une meilleure entropie.

De plus, votre fonction se compose en réalité de deux parties: la génération aléatoire de $nbLetters chiffres et encodage en base62. Cela rendra les choses beaucoup plus claires pour un programmeur de maintenance (peut-être vous!) Qui le rencontrera quelques années plus tard:

// In a class somewhere
private $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";

private function getBase62Char($num) {
    return $chars[$num];
}

public function generateRandomString($nbLetters){
    $randString="";

    for($i=0; $i < $nbLetters; $i++){
        $randChar = getBase62Char(mt_Rand(0,61));
        $randString .= $randChar;
    }

    return $randomString;
}
4
Drew Stephens

Remplacez Rand(0,61) par mt_Rand(0, 61) et ça devrait aller (puisque mt_Rand Est meilleur pour produire des nombres aléatoires) ...

Mais plus important que la force du sel est la façon dont vous le hachez. Si vous avez une bonne routine de sel, mais que vous ne faites que md5($pass.$salt), vous jetez le sel. Je recommande personnellement d'étirer le hachage ... Par exemple:

function getSaltedHash($password, $salt) {
    $hash = $password . $salt;
    for ($i = 0; $i < 50; $i++) {
        $hash = hash('sha512', $password . $hash . $salt);
    }
    return $hash;
}

Pour plus d'informations sur l'étirement du hachage, consultez this SO answer ...

4
ircmaxell

Voici une bien meilleure façon si vous avez des fenêtres et que vous ne pouvez pas faire/dev/random.

//Key generator
$salt = base64_encode(openssl_random_pseudo_bytes(128, $secure));
//The variable $secure is given by openssl_random_ps... and it will give a true or false if its tru then it means that the salt is secure for cryptologic.
while(!$secure){
    $salt = base64_encode(openssl_random_pseudo_bytes(128, $secure));
}
2
tor

C'est ma méthode, elle utilise des nombres vraiment aléatoires provenant du bruit atmosphérique. Tout est mélangé avec des valeurs et des chaînes pseudo-aléatoires. Mélangé et haché. Voici mon code: je l'appelle exagéré.

<?php
function generateRandomString($length = 10) {
    $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    $randomString = '';
    for ($i = 0; $i < $length; $i++) {
        $randomString .= $characters[Rand(0, strlen($characters) - 1)];
    }
    return $randomString;
}

function get_true_random_number($min = 1, $max = 100) {
    $max = ((int) $max >= 1) ? (int) $max : 100;
    $min = ((int) $min < $max) ? (int) $min : 1;
    $options = array(
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HEADER => false,
        CURLOPT_FOLLOWLOCATION => true,
        CURLOPT_ENCODING => '',
        CURLOPT_USERAGENT => 'PHP',
        CURLOPT_AUTOREFERER => true,
        CURLOPT_CONNECTTIMEOUT => 120,
        CURLOPT_TIMEOUT => 120,
        CURLOPT_MAXREDIRS => 10,
    );

    $ch = curl_init('http://www.random.org/integers/?num=1&min='
        . $min . '&max=' . $max . '&col=1&base=10&format=plain&rnd=new');
    curl_setopt_array($ch, $options);
    $content = curl_exec($ch);
    curl_close($ch);

    if(is_numeric($content)) {
        return trim($content);
    } else {
        return Rand(-10,127);
    }
}

function generateSalt() {
    $string = generateRandomString(10);
    $int = get_true_random_number(-2,123);
    $shuffled_mixture = str_shuffle(Time().$int.$string);
    return $salt = md5($shuffled_mixture);
}

echo generateSalt();
?>

Le bruit atmosphérique est fourni par random.org. J'ai également vu une génération vraiment aléatoire à partir d'images de lampes à lave qui sont interprétées via la teinte et l'emplacement. (Hue est l'emplacement)

2
user3483494

J'utilise ceci:

$salt = base64_encode(mcrypt_create_iv(PBKDF2_SALT_BYTES, MCRYPT_DEV_URANDOM));
1
William Wallace

Une technique assez simple:

$a = array('a', 'b', ...., 'A', 'B', ..., '9');
shuffle($a);
$salt = substr(implode($a), 0, 2);  // or whatever sized salt is wanted

Contrairement à uniqid (), il génère un résultat aléatoire.

1
Jay

Je pense qu'un très bon sel par exemple est le nom d'utilisateur (si vous parlez de hachage pw et que le nom d'utilisateur ne change pas.)

Vous n'avez rien à générer et vous n'avez pas besoin de stocker d'autres données.

1
NikiC

Si vous voulez un sel unique ultime, vous devez utiliser une valeur unique entrée et requise par l'utilisateur, comme l'e-mail ou le nom d'utilisateur, puis le hacher à l'aide de sha1, puis le fusionner - concaténer - avec la valeur de sel générée par votre code.

Un autre, vous devez étendre $charUniverse au moyen de certains caractères spéciaux tels que @,!#- etc.

0
SaidbakR