Token anti CSRF sur les formulaires
Résoluyg_be Messages postés 23541 Date d'inscription Statut Contributeur Dernière intervention -
Bonjour,
J'essaye de protéger mes formulaires (d'abord celui du login) avec un token anti CSRF pour cela j'ai trouvé un tuto pas mal :
http://sdz.tdct.org/sdz/securisation-des-failles-csrf.html
J'ai essayé d'adapter mon code en ajoutant des tokens mais ça ne fonctionne pas, j'ai l'impression que je n'arrive pas à lire le token ou peut être même à le générer.
Mon code :
Ma page index.php c'est ici que je gère mes urls je me suis dit que c'était le bon endroit pour déclarer mes deux fonctions :
//Anti CSRF function generer_token($nom = '') { session_start(); $token = uniqid(rand(), true); $_SESSION[$nom.'_token'] = $token; $_SESSION[$nom.'_token_time'] = time(); return $token; } function verifier_token($temps, $referer, $nom = '') { session_start(); if(isset($_SESSION[$nom.'_token']) && isset($_SESSION[$nom.'_token_time']) && isset($_POST['token'])){ if($_SESSION[$nom.'_token'] == $_POST['token']){ if($_SESSION[$nom.'_token_time'] >= (time() - $temps)){ if($_SERVER['HTTP_REFERER'] == $referer){ return true; } } } } else{ return false; } }
Ma page login.view.php (mon formulaire de login) :
<?php ob_start(); $token = generer_token('connexion'); // Ge génère mon token ici ?> <script src="https://www.google.com/recaptcha/api.js" async defer></script> <div class="container col-xl-10 col-xxl-8 px-4 py-0"> <div class="row align-items-center g-lg-5 py-5"> <div class="col-lg-7 text-center text-lg-start"> <h1 class="display-4 fw-bold lh-1 mb-3">PORTAIL</h1> <p class="col-lg-10 fs-4">Bienvenue sur le portail. N'hésitez pas à nous faire un retour si vous avez rencontré des problèmes lors de la navigation.<br> Bonne journée à vous.<br> Cordialement, la cellule SI. </br> </div> <div class="col-md-10 mx-auto col-lg-5"> <form class="p-4 p-md-5 border rounded-3 bg-light" method="POST" action="<?= URL ?>validation_login"> <div class="form-floating mb-3"> <input type="text" class="form-control" id="login" placeholder="AAAA0000" name="login" required> <label for="login">CUID</label> </div> <div class="form-floating mb-3"> <input type="password" class="form-control" id="password" name="password" placeholder="Mot de passe" required> <label for="password">Mot de passe</label> </div> <button class="w-100 btn btn-lg btn-primary bouton border-0" id="vdcsolo" type="submit">Connexion</button> <hr class="my-4"> <a href="<?= URL ?>users/motdepasse"><small class="text-muted">Pour un mot de passe oublié, cliquez ici.</small></a> <div class="g-recaptcha" data-sitekey="xxx"></div><br><br> <!-- Anti CSRF---> <div><input type="hidden" name="token" id="token" value="<?php echo $token; ?>"></div> <!---Le champ caché a pour valeur le jeton </form> </div> </div> </div> <?php $titre = ""; $content = ob_get_clean(); require "template.php"; ?>
Mon controller userController.php :
public function validation_login($login, $password) { if(verifier_token(60, 'http://localhost/portail', 'connexion')) { if ($this->userManager->isCombinaisonValide($login, $password)) { Toolbox::ajouterMessageAlerte("Bon retour sur le portail " . $login . " ! AJOUT DU SYSTEME DE FILTRE POUR LES INCIDENTS" , Toolbox::COULEUR_VERTE); $_SESSION['profil'] = [ "login" => $login, ]; $datas = $this->userManager->getUserInformation($_SESSION['profil']['login']); $_SESSION['profil']["bl"] = $datas['bl']; $_SESSION['profil']["dist"] = $datas['dist']; $_SESSION['profil']["admin"] = $datas['admin']; $_SESSION['profil']["demandevalidation"] = $datas['demandevalidation']; $_SESSION['profil']["pilote"] = $datas['pilote']; $_SESSION['profil']["rs"] = $datas['rs']; if (Securite::estConnecte() && Securite::estBl()) { header("Location: " . URL . "tngs"); } else { header("Location: " . URL . "accueil"); } } else { Toolbox::ajouterMessageAlerte("Combinaison Login / Mot de passe non valide", Toolbox::COULEUR_ROUGE); header("location: " . URL); var_dump($password); } } else { Toolbox::ajouterMessageAlerte("Le token de connexion n'est plus valide", Toolbox::COULEUR_ROUGE); header("location: " . URL); } }
Mon erreur :
Il affiche ce Toolbox :
Toolbox::ajouterMessageAlerte("Le token de connexion n'est plus valide", Toolbox::COULEUR_ROUGE);
Je ne sais pas d'où ça peut venir, si vous avez des idées n'hésitez pas !
Cdt
- Le jeton csrf est invalide. veuillez renvoyer le formulaire.
- Whatsapp formulaire opposition - Guide
- Formulaire de réclamation facebook - Guide
- Jeton rouge avis - Forum Consommation & Internet
- Confirmer le nouvel envoi du formulaire err_cache_miss - Forum Webmastering
- Civilité sur un formulaire - Forum VB / VBA
6 réponses
bonjour,
Tu fais plusieurs tests dans verifier_token(), il serait utile de déterminer le résultat de chacun.
Le voilà :
Notice: Undefined variable: nom in C:\Applications\wamp64\www\portail\controllers\UserController.controller.php on line 232
Notice: Undefined index: _token in C:\Applications\wamp64\www\portail\controllers\UserController.controller.php on line 232
NULL
Notice: Undefined variable: nom in C:\Applications\wamp64\www\portail\controllers\UserController.controller.php on line 233
Notice: Undefined index: _token_time in C:\Applications\wamp64\www\portail\controllers\UserController.controller.php on line 233
NULL string(33) " 5557175956349514ed2bfa1.47271953"
Dans la fonction validation_login non
Mais on s'en sert dans la fonction generer_token() et verifier_token().
Elle me permet de différencier les différents token car pour l'instant j'en ai qu'un pour ma page de connexion mais après je voudrais en mettre sur chaque page où il y a un formulaire.
A noter la fonction generer_token génère bien un token.
Re, j'ai oublié de préciser que j'suis en alternance.
J'ai ajouté ces deux var_dump() dans ma fonction de connexion :
public function isCombinaisonValide($login, $password) { $passwordBD = $this->getPasswordUser($login); if(verifier_token(60, 'http://localhost/portail', 'connexion')) { // Code erreur: #UM04 if (password_verify($password, $passwordBD)) { return true; } else { // à décommenter lors du passage en prod et commenter la ligne en dessous Toolbox::ajouterMessageAlerte("Echec lors de la connexion", Toolbox::COULEUR_ROUGE); Toolbox::ajouterMessageAlerte("Echec de la comparaison - Code erreur: #UM04", Toolbox::COULEUR_ROUGE); return false; } } else { var_dump(verifier_token(60, 'http://localhost/portail_bl_rs', 'connexion')); var_dump($_SESSION); Toolbox::ajouterMessageAlerte("Le token de connexion n'est plus valide", Toolbox::COULEUR_ROUGE); } }
Qui m'affiche :
NULL array(2) { ["connexion_token"]=> string(32) "720438949635662224cd697.90031424" ["connexion_token_time"]=> int(1666605602) }
Le var_dump() de ma fonction verifier_token() me renvoit : NULL et le var_dump() de $_SESSION me renvoit :
array(2) { ["connexion_token"]=> string(32) "720438949635662224cd697.90031424" ["connexion_token_time"]=> int(1666605602) }
J'ai ajouté ce que tu m'as dit
public function isCombinaisonValide($login, $password) { $passwordBD = $this->getPasswordUser($login); if(verifier_token(60, 'http://localhost/portail', 'connexion')) { // Code erreur: #UM04 if (password_verify($password, $passwordBD)) { return true; } else { // à décommenter lors du passage en prod et commenter la ligne en dessous Toolbox::ajouterMessageAlerte("Echec lors de la connexion", Toolbox::COULEUR_ROUGE); Toolbox::ajouterMessageAlerte("Echec de la comparaison - Code erreur: #UM04", Toolbox::COULEUR_ROUGE); return false; } } else { var_dump(verifier_token(60, 'http://localhost/portail', 'connexion')); var_dump($_SESSION); var_dump($_POST['token']); echo time(); var_dump($_SERVER['HTTP_REFERER']); Toolbox::ajouterMessageAlerte("Le token de connexion n'est plus valide", Toolbox::COULEUR_ROUGE); } }
ce que ça m'affiche :
NULL array(2) { ["connexion_token"]=> string(32) "266224583635679b1976174.65750909" ["connexion_token_time"]=> int(1666611633) } string(33) " 266224583635679b1976174.65750909" 1666611638 string(31) "http://localhost/portail/"
Tout à l'air bon j'ai l'impression que c'est juste ma fonction le problème
J'comprend pas pourquoi ma fonction me retourne la valeur null alors que toutes les infos nécessaires que j'ai var_dump() en dessous du var_dump() de ma fonction retournent les bonnes infos ? Le $_SESSION reprend bien la valeur du connexion_token qui est bien identique au $_POST['token'].
Qu'est *-ce qui ne va pas ?? help please
Ok j'viens de regarder en effet ce ne sont pas les mêmes pour
var_dump($_SESSION); var_dump($_POST['token']);
J'ai :
array(2) { ["connexion_token"]=> string(33) "20856374206357c932f3d595.05367174" ["connexion_token_time"]=> int(1666697522) } string(34) " 20856374206357c932f3d595.05367174"
Déjà est-ce normal d'avoir un int dans connexion_token_time ? Oui il me semble car ça représente le time() de la génération du token.
Mais var_dump($_POST['token']) devrait renvoyer le connexion_token aussi pas le connexion_token_time
Et pourquoi y 'a un un espace dans le connexion_token_time ??
J'sais pas si j'ai bien compris mais j'ai fais comme ça :
var_dump($_SESSION); echo '<br><br>'; var_dump($_POST['token']);
et j'ai ça :
array(2) { ["connexion_token"]=> string(33) "10129750246357e657b5d463.30582692" ["connexion_token_time"]=> int(1666704983) }
string(34) " 10129750246357e657b5d463.30582692"
Donc $_SESSION retourne le connexion_token et le connexion_token_time
Et $_POST['token'] retourne le connexion token avec un espace au début de la chaîne en plus.
Vous n’avez pas trouvé la réponse que vous recherchez ?
Posez votre questionOk re y'a du nouveau ! J'ai pas pu répondre hier pcq j'ai trouvé la solution et je devais filer juste après,
En gros je crois qu'il y avait deux problèmes.
Le premier dans le paramètre de ma fonction :
http://localhost/portail
n'était pas bon il fallait ajouté un / à la fin :
http://localhost/portail/
Ensuite c'est du côté du formulaire quand je met dans mon champ caché mon token un espace c'était glissé dedans :
<input type="hidden" name="token" id="token" value="(ici un espace)<?php echo $token; ?>"/>
J'aurais pas pensé qu'un espace entre le = et <?php... stockerait un mdp de passe dans mon token mais bon apparemment si !
En tout cas merci pour l'aide c'est cool, maintenant j'ai plus qu'à en mettre sur tous les formulaires !
Re, c'est encore moi je me posais une question pour la suite.
Pour les autres formulaires de mon site, dois-je pour chaque formulaire générer un nouveau token propre à ce formulaire ou toujours garder le même token (celui de la connexion)?
Donc pour les autres formulaires :
Le submit si mon token de connexion est toujours valide ?
Le submit si mon nouveau token générer uniquement pour ce formulaire est valide ?
Ou peut-être autrement, perso par rapport au tuto que j'ai utilisé j'ai compris qu'il fallait générer un token propre à chaque formulaire, mais peut-être que je me trompe ?
Cdt
Le code vérifie que le token n'est pas périmé. Il est donc nécessaire de recréer régulièrement le token, qu'il ait le même nom ou pas.
Je pense qu'il est plus simple d'utiliser un token par formulaire. Cela permet aussi d'ajuster la durée de vie du token en fonction du temps nécessaire pour remplir le formulaire.
Bonjour,
J'ai mis en commentaire à quoi correspond la fonction et les différents if :
Quand tu exécutes le programme, quel est le résultat de chacun de ces tests? Vrai ou faux?
Le problème comme je l'ai dit c'est qu'il m'affiche mon else avec ce message d'erreur :
C'est a dire que au niveau de mon controller et de ma fonction la condition if n'est pas remplie:
Il échoue ou alors n'arrive pas à vérifier mon token et j'aimerais savoir pourquoi.
Si tu veux savoir pourquoi, il faut soit modifier ta fonction verifier_token(), de façon à ce qu'elle donne des informations sur les résultats des tests qu'elle effectue, soit afficher les données qu'elle utilise.