Objectifs de la séance ¶
- Écrire une classe abstraite et la dériver
- Effectuer une authentification sécurisée
Consignes ¶
Ce sujet complémentaire est la suite du TP « Authentification et sessions ». Les questions seront logiquement traitées dans le même dépôt Git que le sujet principal.
Authentification sécurisée ¶
Le but est de proposer une authentification sécurisée basée sur l'envoi par le serveur d'un challenge unique lors de la construction du formulaire de connexion. Ce challenge est mémorisé dans les données de session. Il est donc connu du client et du serveur.
Sur le client, le login et le mot de passe saisis par l'utilisateur sont encodés par SHA512 selon le principe suivant :
code = SHA512( concaténation( SHA512( mot_de_passe ), challenge, SHA512( login ) ) )Le résultat de l'encodage sera affecté dans un champ caché «
code
» du formulaire avant l'envoi de ce dernier. Les champs de formulaire « login
» et « pass
» ne doivent pas faire partie des données envoyées, ils ne comporteront donc pas de nom (attribut HTML
« name
»).
Ainsi, ni le login, ni le mot de passe d'un utilisateur ne circulent en clair sur le réseau et, de plus, les données transmises par le client sont différentes à chaque connexion de l'utilisateur puisque le challenge sera nouveau à chaque production du formulaire. Le serveur peut cependant vérifier le couple identifiant / mot de passe en effectuant le même encodage que le client sur les données de la base de données au moment de sa requête de recherche du couple identifiant / mot de passe.
La logique chronologique des opérations sera la suivante :
- Avant de commencer à travailler sur l'authentification sécurisée, vous allez refactoriser votre classe «
UserAuthentication
» pour en extraire une classe abstraite «AbstractUserAuthentication
» dont elle héritera :- Recopiez votre classe «
UserAuthentication
» en «AbstractUserAuthentication
» - Dans «
UserAuthentication
», supprimez la propriété «$user
» - Dans «
UserAuthentication
», supprimez toutes les méthodes sauf «loginForm()
» et «getUserFromAuth()
» - Dans «
UserAuthentication
», supprimez toutes les constantes sauf «LOGIN_INPUT_NAME
» et «PASSWORD_INPUT_NAME
» - Dans «
UserAuthentication
», supprimez le trait «StringEscaper
» si vous l'avez utilisé - Faites hériter «
UserAuthentication
» de «AbstractUserAuthentication
» - Dans «
AbstractUserAuthentication
», supprimez le corps des méthodes «loginForm()
» et «getUserFromAuth()
» et rendez-les abstraites - Dans «
AbstractUserAuthentication
», supprimez les constantes «LOGIN_INPUT_NAME
» et «PASSWORD_INPUT_NAME
» et changez la visibilité des autres constantes en «protected
»
- Recopiez votre classe «
- Vérifiez que la mécanique d'authentification fonctionne toujours après cette refactorisation importante Remarque importante
Cette refactorisation permet simplement de préserver la méthode d'authentification simple fonctionnelle et visible pendant que vous implémentez la version sécurisée. Dans la réalité, la variante simple ne serait plus utilisée si vous aviez codé la version sécurisée qui est bien plus pertinente.
- Déposez la classe «
Random
» (télécharger) dans le répertoire «src/Helper
» de votre projet - Téléchargez le fichier «
sha512.js
» (c'est un script du projetcrypto-js
) et placez-le dans un répertoire «js
» à la racine de l'espace web de votre projet - Créez la classe «
SecureUserAuthentication
» qui hérite de «AbstractUserAuthentication
» - Ajoutez-lui les constantes suivantes qui seront utiles pour la suite :
CODE_INPUT_NAME = 'code'
SESSION_CHALLENGE_KEY = 'challenge'
RANDOM_STRING_SIZE = 16
- Implémentez la méthode d'instance «
loginForm(string $action, string $submitText='OK')
» dans sa variante sécurisée :- Faites un tirage aléatoire de taille «
RANDOM_STRING_SIZE
» à l'aide de la classe «Helper\Random
» et stockez la valeur à la clé «SESSION_CHALLENGE_KEY
» dans la session - Produisez un formulaire contenant les champs de « login » (identifié, non nommé), « mot de passe » (identifié, non nommé) et « challenge » (identifié, non nommé et caché, ayant pour valeur la chaîne aléatoire) ainsi qu'un champ pour le « code » (identifié, nommé et caché)
- Insérez le script «
js/sha512.js
» dans la production de votre méthode - Insérez dans la production de votre méthode, un script qui capture l'événement «
onsubmit
» du formulaire et effectue le codage SHA512 à partir du login, du mot de passe et du challenge et stocke la valeur dans code avant d'envoyer le formulaire
- Faites un tirage aléatoire de taille «
- Copiez votre script «
form.php
» en «secure_form.php
» dans lequel vous instancierez un objet «SecureUserAuthentication
» en lieu et place du «UserAuthentication
» et vous modifierez l'action «auth.php
» en «secure_auth.php
», passée en paramètre de la méthode «loginForm()
» - Testez votre encodage en observant la requête
HTTP
générée, en particulier le contenu du corps de la requête qui contient les données postées : - Créez la méthode «
findByHashedCode(string $code, string $challenge)
» dans la classe «User
» - Implémentez cette méthode avec la même logique que «
findByCredentials()
» mais en utilisant une requête préparée reproduisant le codage et comparant le résultat au code reçu - Implémentez la méthode «
getUserFromAuth()
» dans sa variante sécurisée :- Récupérez la valeur du challenge dans les données de session
- Utilisez la méthode «
findByHashedCredentials(string $hashedCredentials, string $challenge)
» de la classe «User
» - Si le résultat est fructueux, créez une instance de «
User
» et mémorisez-la à l'aide la méthode «setUser()
»
- Copiez votre script «
auth.php
ensecure_auth.php
» dans lequel vous instancierez un objet «SecureUserAuthentication
» en lieu et place du «UserAuthentication
» - Testez votre authentification sécurisée