Bonsoir :)

Encore un problème que je n'arrive pas à résoudre! :(

Je voudrais restreindre la partie administration de mon application par un login et un mot de passe. Je n'arrive pas sécuriser l'espace avec les variables de session. Je travaille avec l'architecture MVC.

Dans mon controller fronted, j'ai ma fonction connexion qui vérifie les données ( login et mot de passe) pour ensuite décider des actions.

Sur ma vue, j'initialise une session en vérifiant si les conditions (l'existence ou si les champs de connexion sont vides).

Les variables ne sont pas stockées et donc mon espace admin n'est pas protégé!

Je ne vois pas l'erreur. Quelqu'un pourrait-il me montrer où çà çà coince? :)

Je vous en remercie d'avance :)

Voici mes codes:

33 réponses


le problème est là ;)
ou peut être plus loin :D
@plus
Pierre

Nin1363
Auteur

Merci Pierrot01 :)
Je crois que j'ai oublié mes codes :(
Les voici!
le controller frontend avec la fonction connection

function connection($login, $password) 
{
    $adminManager = new \Alaska\Blog\Model\AdminManager();
    $adminInfo = $adminManager->checkLogin($_POST['login'], sha1($_POST['password']));

    if (is_array($adminInfo)) {
        session_start();
        $_SESSION['administrateur'] = true;
        $_SESSION['login'] = $adminInfo['login'];
        $_SESSION['password'] = $adminInfo['password'];

        if (isset($_SESSION['administrateur'])) {

            header('Location: index.php?action=listChaptersBackend');

        } else {
            include 'view/frontend/ErrorView.php';
        }
    } else {
        include 'view/frontend/connexionView.php';
    }

}

et le code de la vue de la page d'acceuil du backend avec session_start() au tout début

<?php
session_start();
if ((!isset($_SESSION['administrateur'])) || (empty($_SESSION['administrateur']))) {
    header ('Location: view/frontend/ErrorView');
}
?>

Désolée d'avoir oublié de les publier, la fatigue et le désespoir probablement ;)

Salut,

Premièrement, je trouve que tu appelles plusieurs fois session_start(). Ce n'est pas une erreur dans ton code, juste qu'à mon avis, je l'aurai appelé directement sur ta page index.php une bonne fois pour toute. Tu es sûr qu'il n'y aura aucun problème et ça t'enlève des lignes sur toutes tes pages où tu as besoin de session_start().

Peux tu nous donner le code de $adminManager->checkLogin(). Car on ne sait pas ce qu'il retourne (un tableau apparement, mais la faute peut se trouver à l'intérieur).

Bonjour.
Je ne sais pas quel est ton niveau en PHP, mais il y a certaines choses dans ton code qui devrait te paraître incorrectes.
Mais pour commencer, j'ai une question à te poser, est-ce que tu sauvegardes bien tes utilisateurs en base de données ?
Car tu dis :

Dans mon controller fronted, j'ai ma fonction connexion qui vérifie les données ( login et mot de passe) pour ensuite décider des actions.

Et :

Les variables ne sont pas stockées et donc mon espace admin n'est pas protégé!

Pour vérifier les données postées lors de la connexion, il faut bien que les données d'origines soient sauvegardées quelque part autre qu'en session.
Surtout que dans le code de ta fonction connection, tu définies le mot de passe en session, ce qui est totalement inutile puisque le minimum des informations que tu peux avoir besoin de sauvegarder dans la session correspondant à un utilisateur connecté, sont par exemple la valeur de la clé primaire de son enregistrement de données en BDD de manière à pouvoir récupérer ses données par la suite.
Alors que le mot de passe tu n'en as besoin que dans les cas de connexion ou de modification de celui-ci.
Ensuite les autres données concernant l'utilisateur connecté que tu peux avoir besoin de garder en session sont par exemple son nom d'utilisateur de manière à ne pas faire une requête SQL sur chaque page dans lesquelles tu veux le lui afficher.
Ensuite, ce que je ne comprends pas, c'est pourquoi est-ce que tu envoies à ta méthode checkLogin de ta classe AdminManager le mot de passe déja encrypté ?
Il te suffit de lui passer le mot de passe tel qu'il est posté et la méthode s'occupera elle même de l'encrypter quand elle en aura besoin.
Sinon, pour en revenir à ce que je disais au début, si tu veux que ton application travaille avec la session, je te conseillerais que dans une classe qui est exécuté à chaque fois, tu définisses dans son constructeur par exemple :

if (session_status() == PHP_SESSION_NONE) {
    session_start();
}

Celà t'évitera par exemple de tomber sur l'erreur qui est souvent retournée comme quoi la session est déja démarrée.
Si tu préfères, tu peux même mettre le bout de code ci-dessus dans le premier fichier qui est exécuté par ton serveur (index.php).

Ensuite, quel est l'intérêt de cette condition :

if (isset($_SESSION['administrateur'])) {

Alors que quelques lignes plus haut, tu définis l'index administrateur en session en la définissant à true et que ni sa valeur n'est modifié entre-temps, ni supprimé.
Ce qui concrètement veut dire qu'elle est totalement inutilé puisque tu y rentrera toujours dans la première partie, tu pourrais écrire directement le code suivant, ça aurait exactement le même effet :

$_SESSION['administrateur'] = true;
$_SESSION['login'] = $adminInfo['login'];
header('Location: index.php?action=listChaptersBackend');
Nin1363
Auteur

D'abord, merci pour vos réponses! Je suis totalement débutante en php :)
En réponse à JeremyB,
j'ai mis session_start dans mon fichier index.php.
J'ai mis session_start['administrateur'] sur ma page d'acceuil de la partie admin pour commencer la session administrateur. Cette session dépendant de la vérification du login et mot de passe.
Voici la fonction checkLogin qui est dans le model AdminManager

class AdminManager extends Manager
{
    public function checkLogin($login, $password) 
    {  
        $db = $this->dbConnect();
        $req = $db->prepare('SELECT login, password FROM administrateur WHERE login = ?');
        $req->execute(array($login));
        $admin = $req->fetch();
        $hash = sha1($_POST['password']);

        if ($admin['password'] === $hash) {
            $adminInfo = array(
               'login' => $admin['login']
            );
            return $adminInfo;
        } else {
            return false;
        }
    }

Mon problème est que si je mets l'url de la vue de la page d'acceuil de la partie admin, je n'ai pas de page d'erreur, je suis sur cette page donc ma page n'est pas sécurisée.

En réponse à Lartak,
Je n'ai q'un utilisateur, l'administrateur (c'est un blog d'écrivain qui écrit des chapitres) on peut laisser des commentaires, l'écrivain-administrateur, ajoute, modifie, supprimes ses articles et modère les commentaires.
Donc les données de mon utilisateur sont dans ma base de données (id, nom, mot de passe).
J'envoie le mot de passe encrypté pour le comparer à celui qui est dans ma base de données.
Ensuite

if (isset($_SESSION['administrateur'])) {

            header('Location: index.php?action=listChaptersBackend');

        } else {
            include 'view/frontend/ErrorView.php';
        }
    } else {
        include 'view/frontend/connexionView.php';
    }

Si la session de l'administrateur n'est pas vide alors direction page d'acceuil de l'admin,
sinon erreur de connexion(quelqu'un met l'url du fichier de la page de l'accueil de l'admin, dans la barre d'adresse, c'est interdit!)
sinon mauvais mot login et ou mot de passe, retour à la page de connexion.
Voilà ce que je voudrais faire :)

Je te conseille des modifications, notamment pour prévoir au cas où tu décides par la suite de faire évoluer ton application web en acceptant d'autres utilisateur que toi, ça t'évitera de devoir faire de devoir revoir ton code entier par la suite.

La première est ceci :

$adminInfo = [
    'login' => $admin['login'],
    'administrateur' => true
];

Ensuite toujours dans la même fonction :

$hash = sha1($password);

Il te faut bien sûr ne pas encrypter le mot de passe en la passant dans la fonction depuis la fonction connection.
Cette modification, c'est parce que tu définies un argument à ta fonction checkLogin, alors que tu ne l'utilises pas dans celle-ci.

Pour terminer, concernant la condition :

if ($adminInfo) {
    $_SESSION['administrateur'] = $adminInfo['administrateur'];
    $_SESSION['login'] = $adminInfo['login'];
    header('Location: index.php?action=listChaptersBackend');
}
include 'view/frontend/ErrorView.php';

D'ailleurs, ce que je ne comprends pas, c'est que pour ta fonction connection, tu déclares des arguments à celles-ci, mais tu ne les utilises pas dans celle-ci.
Dans la vue qui fait appel à cette fonction, tu devrais avoir quelque chose comme :

if (!empty($_POST) && (isset($_POST['login'], $_POST['login']) && !empty($_POST['login']) && !empty($_POST['password']))) {
    $uneClasse->connection($_POST['login'], $_POST['password']);
} else {
    include 'view/frontend/connexionView.php';
}

Par contre, tu ne sembles même pas gérer les messages flash concernant les formulaires et la connection.

Nin1363
Auteur

Merci pour ta réponse :)
Donc dans ma fonction checklogin, je dois écrire

 public function checkLogin($login, $password) 
    {  
        $db = $this->dbConnect();
        $req = $db->prepare('SELECT login, password FROM administrateur WHERE login = ?');
        $req->execute(array($login));
        $admin = $req->fetch();
       $hash = sha1($password);
if ($admin['password'] === $hash) {
            $adminInfo =  [
    'login' => $admin['login'],
    'administrateur' => true
];
            return $adminInfo;
        } else {
            return false;
        }

pourquoi $hash n'est pas égal à sha1($_POST['password'] )?

Ensuite, je ne vois pas dans quelle vue je dois mettre

if (!empty($_POST) && (isset($_POST['login'], $_POST['login']) && !empty($_POST['login']) && !empty($_POST['password']))) {
    $uneClasse->connection($_POST['login'], $_POST['password']);
} else {
    include 'view/frontend/connexionView.php';
}

Pas dans connexionView.php, listChaptersViewBackend......
Désolée, je ne vois pas dans quelle vue :(
Et non, je ne connais pas les message flash pour les formulaires et la connexion.
Je vais voir ce que c'est. C'est obligatoire?

pourquoi $hash n'est pas égal à sha1($_POST['password'] )?

Si à l'endroit où tu utilises connection si tu ne lui passes pas la valeur du mot de passe non crypté qui est posté, c'est normal, puisque tu voudrais recrypté une valeur qui est déjà crypté.

Ensuite, je ne vois pas dans quelle vue je dois mettre

Ta méthode/fonction connection tu l'appelles bien quelque part, non ?
Montres nous le code y correspondant à l'appel de la méthode/fonction.

Et non, je ne connais pas les message flash pour les formulaires et la connexion.
Je vais voir ce que c'est. C'est obligatoire?

Ce n'est pas obligatoire, mais c'est préférable car ça permet à l'utilisateur d'avoir un retour visuel, pour en savoir plus : Formation PHP » Mise en pratique de la POO en PHP : Messages flash, si ce n'est pas déjà fait, je te conseille vivement de suivre la formation complète : Formation PHP » Mise en pratique de la POO en PHP.

Nin1363
Auteur

Voici le fichier index.php

<?php

session_start();

require 'controller/frontend.php';
require 'controller/backend.php';

try {
    if (isset($_GET['action'])) {
        if ($_GET['action'] === 'connexion') {
            include 'view/frontend/connexionView.php';
        } elseif ($_GET['action'] === 'login') {
            if (!empty($_POST['login']) AND !empty($_POST['password'])) {
                    connection($_POST['login'], sha1($_POST['password'])); 
            } else {
                throw new Exception('Le login et/ou le mot de passe sont incorrects');
            }

          } elseif ($_GET['action'] == 'deconnexion') {
            logOut();
        }
    } else {
        listChapters();
    } 
}
catch(Exception $e) {
    echo 'Erreur : ' . $e->getMessage();
}

le fichier controller/frontend.php

function connection($login, $password) 
{
    $adminManager = new \Alaska\Blog\Model\AdminManager();
    $adminInfo = $adminManager->checkLogin($_POST['login'], sha1($_POST['password']));

    if (is_array($adminInfo)) {
        session_start();
        $_SESSION['administrateur'] = true;
        $_SESSION['login'] = $adminInfo['login'];
        $_SESSION['password'] = $adminInfo['password'];

            header('Location: index.php?action=listChaptersBackend');

    } else {
        include 'view/frontend/connexionView.php';
    }

}

Le model Model/AdminManager

class AdminManager extends Manager
{
    public function checkLogin($login, $password) 
    {  
        $db = $this->dbConnect();
        $req = $db->prepare('SELECT login, password FROM administrateur WHERE login = ?');
        $req->execute(array($login));
        $admin = $req->fetch();
        $hash = sha1($_POST['password']);

        if ($admin['password'] === $hash) {
            $adminInfo = array(
               'login' => $admin['login']
            );
            return $adminInfo;
        } else {
            return false;
        }
    }

La vue view/connexionView.ph

^<?php $title = 'Administration'; ?>

<?php  ob_start(); ?>

<div class="container">
    <div class="jumbotron jumbotron-fluid mot_de_passe">
        <h1 class="pass"><strong>Mot de passe</strong></h1>
        <h3>Veuillez remplir ces champs avec votre login et votre mot de passe pour accéder à la partie administration de votre site</h3>
        <h6 style="text-align:center">Si votre login et/ou mot de passe est erroné, vous reviendrez sur cette page ;)</h6>
        <form name="password" id="password" action="index.php?action=login" method="post">
            <label for="login">Login : </label>
            <input type="text" name="login" required><br><br>
            <label for="password">Mot de passe : </label>
            <input type="password" name="password" required /><br><br>
            <input type="submit" name="connexion" value="Connexion">
        </form>
    </div>
</div>

<?php  $content = ob_get_clean(); ?>
<?php require 'template.php'; ?>

Dans l'index, j'ai l'action qui définit la fonction à utiliser, qui appelle le controller qui va chercher les données dans le model pour les transmettre à la vue. C'est bien çà?

Pour commencer, dans ton fichier index.php, remplaces connection($_POST['login'], sha1($_POST['password'])); par connection($_POST['login'], $_POST['password']);, soit enlever l'encryptage du mot de passe lorsque tu le passe en argument de la fonction connection.
Ensuite, dans ta fonction connection de ton fichier frontend.php, enlève également l'encryptage du mot de passe lorsque tu le passe à la méthode checkLogin.
Car actuellement tu encrypté le mot de passe à 3 reprises, tu vas donc forcément avoir des soucis pour vérifier la correspondance du mot de passe saisi dans le formulaire et celui qui est stocké en BDD.
Ensuite, dans la condition de ta fonction connection, remplaces if (is_array($adminInfo)) par if ($adminInfo), car vouloir faire la vérification sur le type d'une variable, c'est utile dans le cas ou on veut par exemple vérifier si le type est un tableau par exemple, dans le cas ou on peut se retrouver avec un variable qui contient un objet.
Sauf que dans ton cas, si ce n'est pas un tableau, ça retournera forcément false.
Pour terminer, supprimes le stockage du mot de passe dans la session, comme je te l'ai déjà dit, c'est une donnée que l'on n'abesoin que dans de rares cas, soit par exemple pour sa modification et si tu veux garder le session_start() dans ta fonction connection, utilises de préférence la syntaxe que je t'ai donné pour éviter des risques inutiles, soit :

if (session_status() == PHP_SESSION_NONE) {
    session_start();
}

Car étant donné que tu démarres déja la session dans ton fichier index.php, tu as de gros risques de tomber sur des erreurs portants sur la session par la suite.

Nin1363
Auteur

Bonjour :) Merci pour tes explications!
J'ai fait les changements que tu m'as indiqués, mais j'ai toujours le même problème à savoir que si je mets l'adresse de la page d'acceuil du backend après m'être déconnectée, je tombe toujours sur cette page : (

function connection($login, $password) 
{
    $adminManager = new AdminManager();
    $adminInfo = $adminManager->checkLogin($_POST['login'], ($_POST['password']));

    if ($adminInfo) {

        $_SESSION['administrateur'] = $adminInfo['administrateur'];
        $_SESSION['login'] = $adminInfo['login'];

        header('Location: index.php?action=listChaptersBackend');
    } 
        include 'view/frontend/connexionView.php';
    if (session_status() == PHP_SESSION_NONE) {
        session_start();
    }
}

et j'ai bien enlevé l'encryptage du pasword dans index.php et dans ma fonction checkLogin()

Je te donne l'adresse de mon site ainsi que mon github
http://alaska.artecotek.net/
https://github.com/Nin1963/Projet-4-Blog-d-un-ecrivain

Je te remercie vraiment beaucoup de ta patience, mais j'aimerai vraiment comprendre pourquoi çà ne marche pas.
Merci! :)

Dans controller/frontend.php, la fonction connection() vérifie si $adminInfo contient quelque chose sinon il te retourne le formulaire de connexion.

C'est dans ton model Model/AdminManager que ça merdouille :

<?php
if ($admin['password'] === $hash) {
            $adminInfo = array(
               'login' => $admin['login']
            );
            return $adminInfo;
        } else {
            return false;
        }

Si ça retourne toujours false, c'est que $admin['password'] === $hash n'est pas vrai. Fait des var_dump() sur $admin['password'] et $hash.

Nin1363
Auteur

Je n'obtiens rien à part
Parse error: syntax error, unexpected 'var_dump' (T_STRING), expecting function (T_FUNCTION) or const (T_CONST) in C:\wamp64\www\blog\model\AdminManager.php on line 26

La connexion marche, si je mets le bon login et le bon mot de passe et dans le cas contraire je ne suis pas connectée, donc çà marche.
C'est la session qui ne marche pas, celle qui devrait protéger mes variable de session pour qu'on ne puisse pas aller, quand je me suis déjà déconnectée, sur la page d'accueil du backend seulement en copiant-collant l'url : http://blog/index.php?action=listChaptersBackend.
Je crois que je devrais mettre quelque chose comme

if(isset($_SESSION['administrateur']) && isset($_SESSION['administrateur']['login']) && isset($_SESSION['administrateur']['password'])) {
    session_start();
}

mais je ne sais pas où.... pas dans le fichier connexionView.php, ni dans listChaptersViewBackend.php, AdminManager.php......

Je ne sais plus :(

Tes var_dump() doivent être mis après ceci :

<?php
$db = $this->dbConnect();
        $req = $db->prepare('SELECT login, password FROM administrateur WHERE login = ?');
        $req->execute(array($login));
        $admin = $req->fetch();
        $hash = sha1($_POST['password']);

En mettant bien en die() après pour arrêter le script, comme ceci :

<?php
$db = $this->dbConnect();
        $req = $db->prepare('SELECT login, password FROM administrateur WHERE login = ?');
        $req->execute(array($login));
        $admin = $req->fetch();
        $hash = sha1($_POST['password']);

        var_dump($admin['password']); // la même chose avec $hash
        die();
Nin1363
Auteur

J'ai bien $admin['password'] = $hash

Test : if ($admin['password'] == $hash) à la place de if ($admin['password'] === $hash). Je trouve ça étrange..

Nin1363
Auteur

Pareil
$admin['password'] = $hash

Si ça peut t'aider, j'ai repris ton projet sur GitHub, j'ai fais un refactoring dessus et tu peux le trouver sur GitHub, ainsi que sur Packagist et tu peux en voir le résultat fonctionnel ici : Alaska Blog Exemple.
Pour information, le lien de connexion se situe en bas de la page.

Nin1363
Auteur

Merci beaucoup Lartak d'avoir passé du temps sur mon projet :)
Je vais regardé le code plus en détail et apprendre :)
Merci aussi à JeremyB!

Nin1363
Auteur

Après avoir essayé d'adapter certaines fonctions sur mon projet, je suis encore dans une impasse.

J'obtiens cette erreur:
Fatal error: Uncaught Error: Using $this when not in object context in C:\wamp64\www\blog\controller\frontend.php on line 21
( ! ) Error: Using $this when not in object context in C:\wamp64\www\blog\controller\frontend.php on line 21
Call Stack

Time Memory Function Location

1 0.0330 394576 {main}( ) ...\index.php:0
2 0.0360 460488 connection( ) ...\index.php:16

qui vient de cette fonction

function connection($login, $password) 
{
    $adminManager = new AdminManager();
    $adminInfo = $adminManager->checkLogin($login, $password);

    if ($adminInfo) {

        $_SESSION['administrateur'] = true;
        $_SESSION['login'] = $adminInfo['login'];
        $this->setFlash('Vous êtes à présent connecté', 'info');
        header('Location: index.php?action=listChaptersBackend');
    } else {
        $this->setFlash('Identifiants incorrects', 'warning');
        include 'view/frontend/connexionView.php';
    }
}

En tous cas encore merci pour le travail que tu as fait, ton site est vraiment bien! Sauf que je ne peux pas l'utiliser parce que mon projet a déjà été livré et il faut absolument que j'y intègre les sessions.
Ton site en tous cas va me servir de référence pour la suite.
Encore MERCI!!!!!! :)

Tout simplement car ta fonction connection() ne fait pas partie d'une class. Donc tu ne peux pas appeler $this->...

J'obtiens cette erreur:
Fatal error: Uncaught Error: Using $this when not in object context in C:\wamp64\www\blog\controller\frontend.php on line 21
( ! ) Error: Using $this when not in object context in C:\wamp64\www\blog\controller\frontend.php on line 21

Parce que la méthode setFlash se trouve dans la classe Manager sur mon projet et les classes Backend et Frontend étendent de celle-ci, il est donc possible de pouvoir utiliser la méthode avec $this car $this se reporte à la classe courante et les classes dont elle étend, mais pour que ça puisse fonctionner, il faut en premier lieu, que le fichier dans lequel tu l'utilises, soit une classe.

Nin1363
Auteur

çà marche!!!!!! :) les messages avec la méthode setFlash :)
Merci pour tes explications Lartak, j'ai mis un peu de temps mais j'ai compris :)

Par contre, je crois que j'ai toujours le problème des sessions et sur ton site aussi.
Quand je me déconnecte, normalement la session ['admin'] doit se détruire. Mais quand je mets l'url du backend
http://blog/index.php?action=listChaptersBackend ou https://alaska.lartak.fr/?action=dashboard
je suis sur la page admin.
Même quand, je ferme le navigateur. C'est normal?

Merci à toi aussi JeremyB :)

Par contre, je crois que j'ai toujours le problème des sessions et sur ton site aussi.

Ce n'est pas un problème de session, c'est juste que j'avais implémenté une méthode pour la vérification si l'utilisateur est admin, mais je ne l'avais utilisé que dans certaines actions, je viens de faire la modification en n'utilisant la classe Backend que lorsque l'utilisateur accéde à une action réservé à l'administration et en ajoutant un message Flash et une redirection, voir ici.
C'est aussi fonctionnel sur le site d'exemple.

Nin1363
Auteur

Bonsoir :)
Pour la nième fois merci Lartak! :)
Après avoir essayé depuis ce matin d'implanter ta méthode de vérification , je n'y suis toujours pas arrivée. J'ai fait un commit pour te faire voir mes changements. les commentaires setFlash s'affichent très bien, les sessions ne sont toujours pas prises en compte. Je voulais mettre en ligne, mais là aussi j'ai des soucis. Pas facile la vie de débutant en php ;)
Je vais encore travailler ce soir, et demain...... En espérant y arriver.

Nin1363
Auteur

Ps: J'ai une grosse envie de copier-coller ton code....... Mais non, il faut que je comprenne! :)

Rien à voir, mais tu suis une formation pour devoir mettre ton code en ligne ?

Nin1363
Auteur

Oui! Developpeuse Web Junior

Je vois, c'est une formation d'OpenClassrooms, je comprends maintenant d'où vient le préfixe Projet 4 dans le nom de ton dépôt sur GitHub.

Nin1363
Auteur

:) Oui, c'est çà. Tu connais? Qu'en penses-tu?

Nin1363
Auteur

J'ai passé ma soutenance avec ce projet, il ne me manque plus qu'à sécuriser les sessions et elle sera validée. Je pourrai partir en vacances tranquille ;)
En septembre, j'attaque mon dernier projet.

Nin1363
Auteur

Mon site est en ligne :)
http://alaska.artecotek.net/
Avec le message "vous n'êtes pas autorisé à acceder à cette page" , et les sessions qui ne marchent toujours pas :(
C'est parti pour une journée pour régler ce problème .....

Nin1363
Auteur

Bonsoir :) je n'y suis pas arrivée..... Je règlerai çà en septembre :)
Je vous souhaite de bonnes vacances!!!!
Encore merci Lartak et Jeremy B!!!! :)