Token anti CSRF sur les formulaires

Résolu/Fermé
llaumegui - Modifié le 12 oct. 2022 à 11:42
yg_be Messages postés 23408 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 26 décembre 2024 - 27 oct. 2022 à 11:14

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

6 réponses

yg_be Messages postés 23408 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 26 décembre 2024 Ambassadeur 1 557
12 oct. 2022 à 17:06

bonjour,

Tu fais plusieurs tests dans verifier_token(), il serait utile de déterminer le résultat de chacun.

0

Bonjour, 

J'ai mis en commentaire à quoi correspond la fonction et les différents if :

//Cette fonction vérifie le token
//Vous passez en argument le temps de validité (en secondes)
//Le reféré attendu (adresse absolue)
//Le nom optionnel si vous en avez défini un lors de la création du token
function verifier_token($temps, $referer, $nom = '')

{
    session_start();

    //Si le jeton est présent dans la session et dans le formulaire
    if(isset($_SESSION[$nom.'_token']) && isset($_SESSION[$nom.'_token_time']) && isset($_POST['token'])){

        //Si le jeton de la session correspond à celui du formulaire
        if($_SESSION[$nom.'_token'] == $_POST['token']){

            //Si le jeton n'est pas expiré
            if($_SESSION[$nom.'_token_time'] >= (time() - $temps)){

                //Si le référé est bon
                if($_SERVER['HTTP_REFERER'] == $referer){

                    return true;

                }

            }

        }      

    }

    else{

        return false;

    }   

}
0
yg_be Messages postés 23408 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 26 décembre 2024 1 557 > llaumegui
13 oct. 2022 à 11:39

Quand tu exécutes le programme, quel est le résultat de chacun de ces tests?  Vrai ou faux?

0
llaumegui > yg_be Messages postés 23408 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 26 décembre 2024
13 oct. 2022 à 13:38

Le problème comme je l'ai dit c'est qu'il m'affiche mon else avec ce message d'erreur : 

Toolbox::ajouterMessageAlerte("Combinaison Login / Mot de passe non valide", Toolbox::COULEUR_ROUGE);

C'est a dire que au niveau de mon controller et de ma fonction la condition if n'est pas remplie: 

public function validation_login($login, $password)

    {

        if(verifier_token(60, 'http://localhost/portail', 'connexion'))

        {
           ...

Il échoue ou alors n'arrive pas à vérifier mon token et j'aimerais savoir pourquoi.

0
yg_be Messages postés 23408 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 26 décembre 2024 1 557 > llaumegui
13 oct. 2022 à 21:42

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.

0

J'ai mis des var_dump au niveau de mon else de ma fonction validation_login

var_dump($_SESSION[$nom.'_token']);
var_dump($_SESSION[$nom.'_token_time']);
var_dump($_POST['token']);

Le message d'erreur : 

Les lignes 231 à 233 sont mes var_dump().

Il reconnait même pas mes variables help please

0
yg_be Messages postés 23408 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 26 décembre 2024 1 557
14 oct. 2022 à 14:07

Merci de partager le texte du message d'erreur, pas des images.

0
llaumegui > yg_be Messages postés 23408 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 26 décembre 2024
14 oct. 2022 à 14:10

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" 
0
yg_be Messages postés 23408 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 26 décembre 2024 1 557
14 oct. 2022 à 14:11

La variable $nom est-elle réellement utilisée dans cette fonction?

0
llaumegui > yg_be Messages postés 23408 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 26 décembre 2024
Modifié le 14 oct. 2022 à 14:18

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.

0
yg_be Messages postés 23408 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 26 décembre 2024 1 557 > llaumegui
14 oct. 2022 à 14:33

Tu ne peux pas utiliser dans une fonction des variables déclarées ailleurs.

0

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) } 
0
yg_be Messages postés 23408 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 26 décembre 2024 1 557
24 oct. 2022 à 12:11

Il faut également afficher les valeurs de :

$_POST['token]
time()
$_SERVER['HTTP_REFERER']
0
llaumegui > yg_be Messages postés 23408 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 26 décembre 2024
24 oct. 2022 à 13:42

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 

0
yg_be Messages postés 23408 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 26 décembre 2024 1 557 > llaumegui
Modifié le 24 oct. 2022 à 15:55

Tu n'as pas remarqué la différence entre le token enregistré et celui de retour du client?

EDIT: Ni la différence entre le second paramètre de la fonction et la valeur réelle?

0
llaumegui > yg_be Messages postés 23408 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 26 décembre 2024
24 oct. 2022 à 16:22

Je vois la différence entre les 2 mais la je t'avoue que je n'y comprend plus grand choses et pour le second paramètre c'est bien ça qu'il me retourne avec le var_dump($_SERVER['HTTP_REFERER']) :

"http://localhost/portail/"
0
yg_be Messages postés 23408 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 26 décembre 2024 1 557 > llaumegui
24 oct. 2022 à 16:34

Que ne comprends-tu pas?

0

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 

0
yg_be Messages postés 23408 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 26 décembre 2024 1 557
25 oct. 2022 à 12:03

Deux infos ne sont pas "bonnes".  

  1. Le $_SESSION reprend une valeur du connexion_token qui est différente du $_POST['token']. 
  2. $_SERVER['HTTP_REFERER'] est différent de la valeur passée en paramètre.
0
llaumegui > yg_be Messages postés 23408 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 26 décembre 2024
25 oct. 2022 à 13:45

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 ??

0
yg_be Messages postés 23408 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 26 décembre 2024 1 557 > llaumegui
25 oct. 2022 à 14:40

Ajoute peut-être des echo entre les var_dump(), cela t'aidera à comprendre ce qui est affiché.

0
llaumegui > yg_be Messages postés 23408 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 26 décembre 2024
25 oct. 2022 à 15:40

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.

0
yg_be Messages postés 23408 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 26 décembre 2024 1 557 > llaumegui
25 oct. 2022 à 16:03

Et donc ta vérification ne fonctionne pas:

  1. le connexion_token n'est pas le même dans $_SESSION et dans $_POST
  2. $_SERVER['HTTP_REFERER'] est différent de la valeur passée en paramètre
0

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question

Ok 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 ! 

0

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

0
yg_be Messages postés 23408 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 26 décembre 2024 1 557
27 oct. 2022 à 10:45

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. 

0
llaumegui > yg_be Messages postés 23408 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 26 décembre 2024
27 oct. 2022 à 11:02

Ok, je pense que c'est ce que je vais faire toute façon je ne vois pas trop comment faire autrement.

Merci bien ;)

0
yg_be Messages postés 23408 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 26 décembre 2024 1 557 > llaumegui
27 oct. 2022 à 11:14

Tu pourrais, techniquement, utiliser partout le même "nom" de token (connexion) mais cela rendrait tout plus confus.  

0