Avant-propos
En commençant par la définition de votre table :
- UserID
- Fname
- Lname
- Email
- Password
- IV
Voici les changements :
- Les champs
Fname
, Lname
y Email
sera crypté en utilisant un chiffrement symétrique, fourni par OpenSSL ,
- El
IV
permettra de stocker le vecteur d'initialisation utilisé pour le cryptage. Les exigences en matière de stockage dépendent du chiffrement et du mode utilisés ; nous y reviendrons plus tard.
- El
Password
sera haché en utilisant un à sens unique le hachage du mot de passe,
Cryptage
Chiffre et mode
Le choix du meilleur chiffrement et du meilleur mode de chiffrement dépasse le cadre de cette réponse, mais le choix final affecte la taille de la clé de chiffrement et du vecteur d'initialisation. Pour cet article, nous utiliserons AES-256-CBC qui a une taille de bloc fixe de 16 octets et une taille de clé de 16, 24 ou 32 octets.
Clé de cryptage
Une bonne clé de chiffrement est un blob binaire généré par un générateur de nombres aléatoires fiable. L'exemple suivant est recommandé (>= 5.3) :
$key_size = 32; // 256 bits
$encryption_key = openssl_random_pseudo_bytes($key_size, $strong);
// $strong will be true if the key is crypto safe
Cette opération peut être effectuée une ou plusieurs fois (si vous souhaitez créer une chaîne de clés de cryptage). Gardez ces clés aussi privées que possible.
IV
Le vecteur d'initialisation ajoute un caractère aléatoire au chiffrement et est requis pour le mode CBC. Idéalement, ces valeurs ne devraient être utilisées qu'une seule fois (techniquement, une fois par clé de cryptage), de sorte qu'une mise à jour de n'importe quelle partie d'une ligne devrait la régénérer.
Une fonction est fournie pour vous aider à générer le VI :
$iv_size = 16; // 128 bits
$iv = openssl_random_pseudo_bytes($iv_size, $strong);
Exemple
Cryptons le champ du nom, en utilisant la méthode précédente. $encryption_key
y $iv
Pour ce faire, nous devons adapter nos données à la taille du bloc :
function pkcs7_pad($data, $size)
{
$length = $size - strlen($data) % $size;
return $data . str_repeat(chr($length), $length);
}
$name = 'Jack';
$enc_name = openssl_encrypt(
pkcs7_pad($name, 16), // padded data
'AES-256-CBC', // cipher and mode
$encryption_key, // secret key
0, // options (not used)
$iv // initialisation vector
);
Exigences de stockage
La sortie chiffrée, comme le IV, est binaire ; le stockage de ces valeurs dans une base de données peut être réalisé en utilisant des types de colonnes désignés tels que BINARY
o VARBINARY
.
La valeur de sortie, comme l'IV, est binaire ; pour stocker ces valeurs dans MySQL, envisagez d'utiliser BINARY
o VARBINARY
colonnes. Si ce n'est pas possible, vous pouvez également convertir les données binaires en une représentation textuelle à l'aide de la fonction base64_encode()
o bin2hex()
En effet, cela nécessite entre 33 % et 100 % d'espace de stockage supplémentaire.
Décryptage
Le décryptage des valeurs stockées est similaire :
function pkcs7_unpad($data)
{
return substr($data, 0, -ord($data[strlen($data) - 1]));
}
$row = $result->fetch(PDO::FETCH_ASSOC); // read from database result
// $enc_name = base64_decode($row['Name']);
// $enc_name = hex2bin($row['Name']);
$enc_name = $row['Name'];
// $iv = base64_decode($row['IV']);
// $iv = hex2bin($row['IV']);
$iv = $row['IV'];
$name = pkcs7_unpad(openssl_decrypt(
$enc_name,
'AES-256-CBC',
$encryption_key,
0,
$iv
));
Cryptage authentifié
Vous pouvez améliorer l'intégrité du texte chiffré généré en ajoutant une signature générée à partir d'une clé secrète (différente de la clé de chiffrement) et du texte chiffré. Avant que le texte chiffré ne soit déchiffré, la signature est d'abord vérifiée (de préférence avec une méthode de comparaison en temps constant).
Exemple
// generate once, keep safe
$auth_key = openssl_random_pseudo_bytes(32, $strong);
// authentication
$auth = hash_hmac('sha256', $enc_name, $auth_key, true);
$auth_enc_name = $auth . $enc_name;
// verification
$auth = substr($auth_enc_name, 0, 32);
$enc_name = substr($auth_enc_name, 32);
$actual_auth = hash_hmac('sha256', $enc_name, $auth_key, true);
if (hash_equals($auth, $actual_auth)) {
// perform decryption
}
Voir aussi : hash_equals()
Hachage
Le stockage d'un mot de passe réversible dans votre base de données doit être évité autant que possible ; vous souhaitez uniquement vérifier le mot de passe plutôt que de connaître son contenu. Si un utilisateur perd son mot de passe, il est préférable de lui permettre de le réinitialiser plutôt que de lui envoyer son mot de passe original (assurez-vous que la réinitialisation du mot de passe ne peut être effectuée que pour une durée limitée).
L'application d'une fonction de hachage est une opération à sens unique ; elle peut ensuite être utilisée en toute sécurité pour la vérification sans révéler les données d'origine ; pour les mots de passe, une méthode de force brute est une approche réalisable pour les découvrir en raison de leur longueur relativement courte et des mauvais choix de mots de passe de nombreuses personnes.
Les algorithmes de hachage tels que MD5 ou SHA1 ont été conçus pour vérifier le contenu des fichiers par rapport à une valeur de hachage connue. Ils sont largement optimisés pour rendre cette vérification aussi rapide que possible tout en restant précis. Étant donné leur espace de sortie relativement limité, il a été facile de construire une base de données avec des mots de passe connus et leurs résultats de hachage respectifs, les tables arc-en-ciel.
L'ajout d'un sel au mot de passe avant de le hacher rendrait une table arc-en-ciel inutile, mais les récentes avancées matérielles ont fait des recherches par force brute une approche viable. C'est pourquoi vous avez besoin d'un algorithme de hachage qui soit délibérément lent et tout simplement impossible à optimiser. Il doit également être capable d'augmenter la charge pour un matériel plus rapide sans affecter la capacité à vérifier les hachages de mots de passe existants, afin d'être à l'épreuve du temps.
Actuellement, deux choix populaires sont disponibles :
- PBKDF2 (Fonction de dérivation de clé basée sur le mot de passe v2)
- bcrypt (alias Blowfish)
Cette réponse utilisera un exemple avec bcrypt.
Génération
Un hachage de mot de passe peut être généré comme suit :
$password = 'my password';
$random = openssl_random_pseudo_bytes(18);
$salt = sprintf('$2y$%02d$%s',
13, // 2^n cost factor
substr(strtr(base64_encode($random), '+', '.'), 0, 22)
);
$hash = crypt($password, $salt);
Le sel est généré avec openssl_random_pseudo_bytes()
pour former un blob aléatoire de données qui est ensuite exécuté à travers base64_encode()
y strtr()
pour correspondre à l'alphabet requis de [A-Za-z0-9/.]
.
El crypt()
effectue le hachage en se basant sur l'algorithme ( $2y$
pour Blowfish), le facteur de coût (un facteur de 13 prend environ 0,40s sur une machine de 3GHz) et le sel de 22 caractères.
Validation
Une fois que vous avez récupéré la ligne contenant les informations sur l'utilisateur, vous validez le mot de passe de cette manière :
$given_password = $_POST['password']; // the submitted password
$db_hash = $row['Password']; // field with the password hash
$given_hash = crypt($given_password, $db_hash);
if (isEqual($given_hash, $db_hash)) {
// user password verified
}
// constant time string compare
function isEqual($str1, $str2)
{
$n1 = strlen($str1);
if (strlen($str2) != $n1) {
return false;
}
for ($i = 0, $diff = 0; $i != $n1; ++$i) {
$diff |= ord($str1[$i]) ^ ord($str2[$i]);
}
return !$diff;
}
Pour vérifier un mot de passe, vous appelez crypt()
mais vous passez le hachage calculé précédemment comme valeur de sel. La valeur de retour donne le même hachage si le mot de passe donné correspond au hachage. Pour vérifier le hachage, il est souvent recommandé d'utiliser une fonction de comparaison à temps constant afin d'éviter les attaques de synchronisation.
Hachage de mot de passe avec PHP 5.5
PHP 5.5 a introduit la fonction fonctions de hachage de mot de passe que vous pouvez utiliser pour simplifier la méthode de hachage ci-dessus :
$hash = password_hash($password, PASSWORD_BCRYPT, ['cost' => 13]);
Et la vérification :
if (password_verify($given_password, $db_hash)) {
// password valid
}
Voir aussi : password_hash()
, password_verify()
3 votes
Les horreurs du cryptage Sha-1 !
0 votes
php.net/manual/fr/faq.passwords.php
9 votes
SHA est un hachage, pas un cryptage. Le point clé est qu'un hachage ne peut pas être inversé en données originales (pas facilement, en tout cas). Vous voulez probablement mcrypt ou si elle n'est pas disponible, je recommande phpseclib - Bien qu'il soit important de noter que toute implémentation purement PHP de quelque chose qui implique beaucoup de mathématiques de bas niveau sera lente... C'est pourquoi j'aime phpseclib, parce qu'il utilise mcrypt en premier s'il est disponible et ne se rabat sur les implémentations PHP qu'en dernier recours.
7 votes
Normalement, vous ne voulez pas être en mesure de décrypter un mot de passe !
1 votes
En fait, il ne faut pas penser au cryptage à ce niveau, mais plutôt au contrôle d'accès, à la confidentialité, à l'intégrité et à l'authentification. Ensuite, vérifiez comment vous pouvez y parvenir, éventuellement en utilisant le cryptage ou le hachage sécurisé. Vous pouvez lire PBKDF2 et bcrypt/scrypt pour comprendre le hachage sécurisé des mots de passe et autres.
0 votes
Vous pouvez utiliser la fonction
password_hash
ypassword_verify
pour saler et hacher automatiquement vos mots de passe. Vous ne voulez pas que vos mots de passe soient décryptables. Hachez-les simplement et utilisez-les. Détruit effectivement le mot de passe original afin que personne ne sache ce qu'il est, mais permet toujours la vérification des mots de passe en comparant le mot de passe d'entrée avec le hachage stocké à l'aide de la fonctionpassword_verify
. Joli. Je ne suis pas sûr du cryptage... c'est pourquoi je suis ici ! :-D