Dans l'ensemble, les comptes des utilisateurs sont gérés de la même façon que les autres données (voir cours précédents)
On ne stocke jamais les mots de passe en clair dans une BD : si notre serveur est compromis, l'attaquant récupère tout !
Pas à prendre à la légère : des fuites de ce genre arrivent tout le temps, même à des grandes compagnies (infographie des plus grosses fuites des dix dernières années)
Il ne faut pas utiliser n'importe quelle fonction de hash :
typiquement md5
, sha1
et sha256
ne sont pas adaptés (en particulier md5
, qui a été
cassé il y a plus de 10 ans)
Ces fonctions sont conçues pour être rapides à exécuter, or on veut une fonction lente (pour gêner l'attaquant au maximum)
Un bien meilleur choix est l'algorithme bcrypt
,
qui est conçu exactement pour ce genre d'application.
(Par exemple, le coût de calcul d'un empreinte est configurable)
Stocker des empreintes ne suffit pas !
Attaque classique dite des rainbow tables : l'attaquant peut utiliser un dictionnaire des empreintes de tous les mots de passe de moins de n caractères
La parade est d'ajouter un sel au mot de passe, c'est-à-dire une chaîne de caractères aléatoire, qu'on stocke avec le mot de passe.
Plus d'explications et d'autres conseils et bonnes pratiques dans cet article
Avec PHP (≥5.5), il est très simple de prendre ces quelques précautions pour manipuler des mots de passe
password_hash()
:
bcrypt
$hash = password_hash("abc1234", PASSWORD_BCRYPT);
false
en cas
de problème)
ressemblant à
$2y$10$UCsnhCjQZ4X7tj36DLSp0OPLzQYDnd7JEg/84DXLUBtHKhfHduG6ycontenant différentes informations :
Il suffit de stocker cette chaîne dans la base de données pour disposer de toutes les informations nécessaires à la vérification ultérieure du mot de passe.
password_verify()
fait tout le travail pour vous,
en utilisant les informations stockées dans la chaîne :
if (password_verify('toto', $hash)) { echo "Le mot de passe est 'toto'\n"; } else { echo "Le mot de passe n'est pas 'toto'\n"; }
Voir si besoin ce post qui explique comment utiliser le hash bcrypt
sur différentes versions de PHP.
Le fait que l'internaute est connecté·e ou non est simplement enregistré dans une variable de session.
Souvent, on mettra un formulaire de connexion sur toutes les pages
Presque toujours, on mettra un formulaire de déconnexion sur toutes les pages
Il est alors recommandé de replacer l'internaute sur la page courante après sa connexion/déconnexion
La meilleure solution est donc de gérer connexion et déconnexion comme des cas particuliers, dès le début du routeur
Cependant pour simplifier il est parfaitement possible de se contenter d'une page de connexion et d'une page de déconnexion, qu'on peut gérer comme les autres pages
Pour empêcher l'accès à certaines pages/actions, il suffit de vérifier pour chaque cas si l'internaute a le droit d'accès, et de générer une page spéciale de type « accès interdit » dans le cas contraire
vérification très simple
zone potentiellement très différente : on utilisera certainement une autre vue et un autre contrôleur
Principe de base de sécurité : une « liste blanche » est plus sûre qu'une « liste noire »
L'idée est qu'il est difficile (voire impossible) de prévoir tous les cas de malveillance
il vaut toujours mieux tout interdire par défaut et autoriser au cas par cas
Dans le contexte de la gestion des accès sur un site, l'application de cette règle revient à refuser par défaut l'accès à tout le monde
Les cas où l'accès est autorisés sont ceux explicitement listés.
Conséquence : on ne peut pas oublier de sécuriser une page (ce qui serait grave, car on pourrait ne pas s'en rendre compte pendant très longtemps).
Si l'inverse se produit, càd qu'on oublie d'autoriser un accès légitime, c'est moins grave, et on s'en rendra vite compte.