8768 sujets

Développement web côté serveur, CMS

Bonjour à tous,

j'ai un ensemble de 3 pages php permettant de saisir des données dans un formulaire. Elles fonctionnent selon le schéma suivant:
page1.php =redirige vers=>page2.php=redirige vers=>page3.php

Pour sécuriser ces pages, je souhaite les protéger par mot de passe. Je me suis servi de ces indications pour protéger l'accès à page1.php, ce qui me donne le schéma suivant:

password.php=//=>page1.php=>page2.php=>page3.php.

Maintenant, je me dis qu'il serait trop beau que page2 et page3 soient miraculeusement protégées en cascade juste parce qu'il faut un mot de passe pour accéder à page1... Est-ce que je dois plutôt recourir à un fichier .htpassword ou y a-t-il une solution php robuste et accessible à un débutant qui me permette de protéger les 3 pages sans que l'utilisateur doive retaper son mot de passe quand il passe d'une page à l'autre?
Modifié par bouffandt (01 Oct 2018 - 13:15)
Salut

Dans ce cas, tu hash le mot passe, tu stock le token en session php et sur chaque page tu le decrypte pour le lire et si c'est le bon pass tu as accès aux contenue. pour le hash, reste sur du md5 c'est plus simple.
Bonsoir,
merci de ta réponse. J'ai commencé par revoir le code de ma page1.php pour hasher le mot de passe. Voici le code php:

<?php
//on se connecte à la base de données

try
{
	$bdd = new PDO('mysql:host=sql.free.fr;dbname=matable;charset=utf8', 'login', 'mdp');
	$bdd->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
catch(Exception $e)
{
	die('Erreur : '.$e->getMessage());
}
//on récupère le hash dans la base de donnée (OK, ça marche):
$hash=$bdd->query('SELECT password FROM psswd WHERE id=1');
// On réceptionne le password entré par l'utilisateur dans la page password.php (OK, ça marche)
$password = $_POST['mot_de_passe'];
//on hash ce mot de passe saisi par l'utilisateur
$mix=md5($password);
//on compare les deux
if ($mix==$hash)
{
//si c'est bon:
	echo "ok";
}
else
{ 
//si c'est pas bon:
echo "erreur";
}
?>

J'ai vérifié manuellement: $mix et $hash sont bien identiques, et identiques au hash du mot de passe situé dans ma base psswd. J'ai vérifié chaque commande séparément... Mais ma page m'affiche quand même "erreur". Smiley bawling

Petit edit
si je remplace la ligne

//on récupère le hash dans la base de donnée (OK, ça marche):
$hash=$bdd->query('SELECT password FROM psswd WHERE id=1');

par

$hash=md5('monsupermotdepasse');

ça fonctionne. Le problème vient dont de la récupération du hash dans ma base psswd. Ca me chagrine de laisser mon mot de passe en clair dans mon code php quand même...
Modifié par bouffandt (02 Oct 2018 - 07:15)
Modérateur
ben non, car $bdd->query renvoie un objet PDOStatement...

Il faut faire un fetch, par exemple:


$hash=$bdd->query('SELECT password FROM psswd WHERE id=1')->fetchColumn();
kustolovic a écrit :
ben non, car $bdd-&gt;query renvoie un objet PDOStatement...

Il faut faire un fetch, par exemple:


$hash=$bdd-&gt;query('SELECT password FROM psswd WHERE id=1')-&gt;fetchColumn();


Oui voila, c'est ce que je me disais. Dans son if il compare un string et un objet. ce qui forcément renvoi false.
JENCAL a écrit :
et si tu fais des var dump il sont vraiment vraiment pareil ?

En effet, ils sont différents:
- le hash du mot de passe envoyé par l'utilisateur donne bien la bonne chaîne de caractère
- le hash du mot de passe récupéré par la base de données me renvoie le texte de ma requête:
object(PDOStatement)#3 (1) { ["queryString"]=> string(37) "SELECT password FROM psswd WHERE id=1" } 

kustolovic a écrit :
ben non, car $bdd->query renvoie un objet PDOStatement...
Il faut faire un fetch

Je l'ai fait => ça marche et les 2 hash renvoient la même chaîne de caractères.
Je m'attaque ce soir à la suite des conseils de JENCAL:
JENCAL a écrit :
tu stock le token en session php et sur chaque page tu le decrypte pour le lire

Merci à vous deux!
J'ai modifié un peu l'architecture de mes pages pour essayer de clarifier tout ça dans ma tête...
J'ai donc :
password.php [on y tape son mot de passe] =/redirection vers/=>checkpassword.php[censée vérifier la validité du mot de passe et ouvrir une session]=> accès à mes pages.

Voici le code php présent dans ma page checkpassword.php:

<?php
//on se connecte à la base de données

try
{
	$bdd = new PDO('mysql:host=sql.free.fr;dbname=mabase;charset=utf8', 'login', 'password');
	$bdd->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
catch(Exception $e)
{
	die('Erreur : '.$e->getMessage());
}
//on récupère le hash dans la base de donnée (OK, ça marche):
$hash=$bdd->query('SELECT password FROM psswd WHERE id=1')->fetchColumn();;
// On réceptionne le password entré par l'utilisateur (OK, ça marche)
$password = $_POST['mot_de_passe'];
//on hash ce mot de passe saisi par l'utilisateur
$mix=md5($password);
//on compare les deux (Ok, $mix et $hash sont bien les mêmes, identiques à la bdd)
if ($mix==$hash)
{
//si c'est bon on ouvre une session
	session_start();
    $_SESSION['password'] = $hash;
echo "Vous êtes connecté!";?>
<input type="button" value="Continuer" onClick="javascript:document.location.href='modification1.php'" />
<?php
}
else
{ //si c'est pas bon, on envoie promener le visiteur
echo "Erreur d'identification. Connectez-vous pour accéder à cette page";
}
?>

J'ai pensé à créer un dossier "sessions" à la racine de mon site.
J'ai cependant un double message d'erreur qui s'affiche:
Warning: session_start(): Cannot send session cookie - headers already sent by (output started at /var/www/sda/0/f/monsite/checkpassword.php:11) in /var/www/sda/0/f/tbouffand/checkpassword.php on line 32
Warning: session_start(): Cannot send session cache limiter - headers already sent (output started at /var/www/sda/0/f/monsite/checkpassword.php:11) in /var/www/sda/0/f/tbouffand/checkpassword.php on line 32

Je n'arrive pas à en trouver l'origine. J'ai fait un var_dump, et $_SESSION['password'] récupère bien le bon mot de passe.
Modifié par bouffandt (03 Oct 2018 - 23:04)
J'ai cherché un peu et trouvé que mes messages d'erreurs pouvaient venir des causes suivantes:
- un "echo"
- un affichage hors PHP (c'est à dire de l'HTML en dehors du code)
- un ou plusieurs retours chariots avant la balise "<?php"
- une fonction qui rencontre une erreur
etc...
Il me semble bien que le retour chariot était présent dans mon code, sans oublier l'html hors php... J'ai donc revu le code de façon à ce qu'il soit 100% php. L'utilisateur ne verra rien et le fichier checkpassword.php va soit le renvoyer sur password.php si le mot de passe est faux, soit l'envoyer sur page1.php si le mot de passe est bon.
Je suis au taf et je ne peux pas uploader mon fichier pour tester ce que ça donne... Mais si vous voyez un truc qui cloche n'hésitez pas à me le dire.
Le code:

<?php
//on se connecte à la base de données
try
{
	$bdd = new PDO('mysql:host=sql.free.fr;dbname=mabase;charset=utf8', 'login', 'password');
	$bdd->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
catch(Exception $e)
{
	die('Erreur : '.$e->getMessage());
}
//on récupère le hash dans la base de donnée:
$hash=$bdd->query('SELECT password FROM psswd WHERE id=1')->fetchColumn();;
// On réceptionne le password entré par l'utilisateur:
$password = $_POST['mot_de_passe'];
//on hash ce mot de passe saisi par l'utilisateur:
$mix=md5($password);
//on compare les deux:
if ($mix==$hash)
{
//si c'est bon on ouvre la session
	session_start();
    $_SESSION['password'] = $hash;
//Redirection du visiteur vers la 1e page
header('Location:page1.php');}
else
{
//Si c'est pas bon, redirection du visiteur vers la page de connexion
header('Location:password.php');
}
?>

Edit: upload fait: plus de message d'erreur. Je m'attaque à la suite plus tard!
Modifié par bouffandt (04 Oct 2018 - 23:03)
Bonjour à tous,
j'ai rajouté en haut de toutes mes pages le code:
<?php
// On prolonge la session
session_start();
// On teste si la variable de session existe et contient une valeur
if(empty($_SESSION['password'])) 
{
  // Si inexistante ou nulle, on redirige vers le formulaire de login
header('Location:password.php');
  exit();
}
?>

et j'ai créé une page deconnection.php qui permet de quitter proprement (j'espère!) la session:

<?php
  // Démarrage ou restauration de la session
  session_start();
  // Réinitialisation du tableau de session
  // On le vide intégralement
  $_SESSION = array();
  // Destruction de la session
  session_destroy();
  // Destruction du tableau de session
  unset($_SESSION);
  //Redirection du visiteur vers la page de connexion
header('Location:password.php');
?>

Elle sera accessible par un bouton "déconnection" sur page1.php
Jusqu'ici tout semble bien fonctionner: aucun message d'erreur, et mes page1, 2 et 3.php ne sont pas accessibles si on n'a pas saisi le mot de passe ou si deconnection.php a été lancé. Merci à JENCAL et kustolovic pour leur aide!

Pour l'heure je ne veux stocker que des informations sans importance sur ces formulaires: un pointage de retour de papiers pour un voyage, essentiellement composé de cases à cocher et avec un champ Nom/Prénom.
Mais mes pages sont-elles assez sécurisées si je voulais ultérieurement stocker les informations plus "sensibles" que mes prestataires me demanderont de leur communiquer (date et lieu de naissance, n°de CI ou de passeport, adresse, allergies...)?
Modérateur
bonjour,

la faille principale de ton système est de fournir un mot de passe unique à tout le monde. C'est un peu comme distribuer des doubles de tes clés de maison sans jamais chercher à les récupérer. À force, c'est un secret de polichinelle.

Un peu comme un code d'entrée d'immeuble, ça évite que n'importe qui entre, mais ce n'est pas un système de sécurité, c'est un système anti-pipi dans la cage d'escalier.

Un système robuste nécessite une clé différente par utilisateur, que tu invalide lorsqu'il ne doit plus avoir accès.

Ensuite le md5 est mort, vive le md5. Ce hachage est considéré comme obsolète, en php utilise plutôt password_hash et password_verify. C'est facile à mettre en place et correspond mieux aux normes actuelles. MD5 est «suffisant» tant que tu ne stockes pas les mot de passe de tes partenaires, mais autant prendre les bonnes habitudes.

Il ne faut pas oublier que tes pages doivent être en https. (au minimum celle où o introduit le mot de passe).

Seulement, il faut évaluer ce que tu entends par «sensible»: Si les données sont celles de tiers, tu as une responsabilité légale (coucou RGPD) de sécurisation et morale envers tes clients/partenaires (que généralement tu ne souhaites pas fâcher).

Si les données sont les tienne, il t'appartiens de décider quel sécurité tu souhaites. Il faut être conscient qu'aucun hacking ne vaut la peine pour récupérer les données d'une seule personne. (Sauf si elles sont suffisamment confidentielles pour un chantage).
Bonsoir et merci de ta réponse,
kustolovic a écrit :

la faille principale de ton système est de fournir un mot de passe unique à tout le monde. C'est un peu comme distribuer des doubles de tes clés de maison sans jamais chercher à les récupérer. À force, c'est un secret de polichinelle.

Un système robuste nécessite une clé différente par utilisateur, que tu invalide lorsqu'il ne doit plus avoir accès.

Nous ne serions que 2 à avoir accès au formulaire. Mais je vois ce que tu veux dire. J'ai donc créé un profil administrateur et un profil utilisateur, chacun avec un mot de passe (l'admin -moi- connaissant le mdp de l'utilisateur mais pas l'inverse).
kustolovic a écrit :
Ensuite le md5 est mort, vive le md5. Ce hachage est considéré comme obsolète, en php utilise plutôt password_hash et password_verify. C'est facile à mettre en place et correspond mieux aux normes actuelles. MD5 est «suffisant» tant que tu ne stockes pas les mot de passe de tes partenaires, mais autant prendre les bonnes habitudes.

J'ai tenté de le mettre en place, mais je me heurte à un petit problème. J'ai modifié mon fichier password.php pour qu'il demande le login de l'utilisateur, comme suit:
<!doctype html>
<html lang="fr">
<head>
<meta charset="utf-8">
<title>Identifiez-vous</title>
<link rel="stylesheet" href="style.css">
<script src="script.js"></script>
<LINK rel=STYLESHEET href="../stylesformulaires.css" type="text/css">
</head>
<body>
			<p>Saisir le mot de passe permettant d'accéder à la liste des participants.</p>
        <form action="checkpassword.php" method="post">
            <p>
			<input type="text" name="login"/>
			<input type="password" name="mot_de_passe"/>
            <input type="submit" value="Valider" />
            </p>
        </form>
</body>
</html>

Puis j'ai essayé de mettre en place le mot de passe avec password_hash:
- dans la base de donnée psswd, j'ai mis les mots de passe respectifs de l'utilisateur et de l'administrateur, hashés avec password_hash.
- ensuite, j'ai tenté de mettre en place tout cela dans mon fichier checkpassword.php, comme ceci:
<?php
//on se connecte à la base de données
try
{
	$bdd = new PDO('mysql:host=sql.free.fr;dbname=mabase;charset=utf8', 'login', 'password');
	$bdd->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
catch(Exception $e)
{
	die('Erreur : '.$e->getMessage());
}
//On réceptionne le login et le password entrés par l\'utilisateur (OK, ça marche)
$password = $_POST['mot_de_passe'];
$login = $_POST['login'];
//on récupère le hash dans la base de donnée
$hash=$bdd->query('SELECT * FROM psswd WHERE nom="$login"')->fetchColumn();
//on hash ce mot de passe saisi par l\'utilisateur
$mix=password_hash($password, PASSWORD_DEFAULT);
//on compare les deux
 if (password_verify($mix, $hash))
{
	session_start();
    $_SESSION['password'] = $hash;
    $_SESSION['login']= $_POST['login'];
//Redirection du visiteur vers la 1e page
header('Location:page1.php');}
else
{
//Redirection du visiteur vers la page de connexion
header('Location:password.php');
}
?>

J'ai un problème qui me semble se focaliser sur la requête sql qui récupère les login et mots de passe dans ma base (pour info, ces 2 colonnes sont en CHAR avec une valeur de 255 caractères). En effet, un var_dump sur $hash renvoie "bool:false". Mais je n'arrive pas à voir d'où provient mon erreur. Je n'ai sans doute pas pris en compte les spécificités de la fonction password_hash.

kustolovic a écrit :
Seulement, il faut évaluer ce que tu entends par «sensible»: Si les données sont celles de tiers, tu as une responsabilité légale (coucou RGPD) de sécurisation et morale envers tes clients/partenaires (que généralement tu ne souhaites pas fâcher).

Précisément, ce sont des données de tiers (dont des mineurs) avec lesquelles il est hors de question de faire preuve de la moindre légèreté... Pour l'instant je ne suis qu'un prof qui organise des voyages scolaires et cherche un outil maison pratique pour centraliser les retours de documents avec mes collègues organisateurs. Pour être en https, il faudrait si j'ai bien compris que j'aie un nom de domaine (là je suis chez free) et que j'achète ou obtienne un certificat SSL. Cela viendra peut-être en son temps, mais pas tout de suite. Si la sécurité actuelle de mes pages suffit (avec le recours à password_hash, j'entends), vais donc pour l'instant me contenter de stocker des informations anodines du genre "papier rendu/pas rendu"
Modifié par bouffandt (05 Oct 2018 - 23:26)
J'ai réglé mon problème de mot de passe qui tenait à une erreur de syntaxe dans la requête sql et au fait de hasher le mot de passe saisi par l'utilisateur. La requête qui fonctionne:

$hash=$bdd->query('SELECT password FROM psswd WHERE nom="'.$login.'"')->fetchColumn();

et le code rectifié (qui marche cette fois) dans checkpassword.php pour comparer le mot de passe et le hash:

//On réceptionne le login et le password entrés par l\'utilisateur
$password = $_POST['mot_de_passe'];
$login = $_POST['login'];
//on récupère le hash du mot de passe dans la base de donnée
$hash=$bdd->query('SELECT password FROM psswd WHERE nom="'.$login.'"')->fetchColumn();
//on compare les deux
 if (password_verify($password, $hash))

Merci en tout cas à JENCAL et Kustolovic pour leurs conseils!
Modifié par bouffandt (08 Oct 2018 - 11:17)