Salut,
J'avais déjà relu tout le tuto en détail et m'étais au préalable renseigné sur certaines fonctions php
qui m'étaient nouvelles, donc j'avais bien en tête, par exemple,
le sens de filter_input.
Je garde bien en tête que le temps que vous me consacrez est précieux pour vous et pour moi,
et ce n'est bien entendu pas dans mon intention de vous demander des choses que je peux vérifier moi-même
(c'est aussi pour cela que j'ai pris soin de relire tous les messages écrits depuis le début, pour
éviter toute répétition de votre part dans la mesure du possible).
Maintenant, mon problème concerne la récupération du grain de sel fixe.
Tout le reste fonctionne merveilleusement bien!
Ton programme Ajax pour aller chercher le grain de sel fixe à l'air plus sécurisé que le miens,
et il part de la fonction javascript ce qui n'était pas non plus le cas du miens.
J'entends très bien ta recommandation que j'utilise le code du tuto "difficile"
pour terminer ma programmation, et j'entends également que dans cet élan
je ne cherche pas coûte que coûte à tout vouloir comprendre tout de suite.
Je suis OK. Si je termine ma programmation, c'est mission accomplie.
Je laisse donc mes quelques autres interrogations sur le sel aléatoire de côté...
Mais j'ai des difficultés pour terminer cette programmation:
A.
Si je veux utiliser la librairie, je suis donc dépendant de Google.
Si demain il y a un problème au sein de la librairie pour x raison
(le code est devenu obsolète par exemple), je suis complètement coincé.
Et si des utilisateurs me font savoir qu'ils n'arrivent pas à se connecter
je ne serai pas à même de réparer une librairie dont je ne connais pas le code,
ce qui est d'autant plus embêtant "s'il faut faire vite" (ce point est très important).
C'est pour cela que je ne peux m'empêcher de penser si je n'aurais pas intérêt à garder
mon programme Ajax qui par ailleurs marche, et à l'adapter pour qu'il fonctionne à partir de la fonction
js avec un grain de sel aléatoire faisant office de token.
B.
Maintenant, car l'idée c'est quand même de travailler à partir du tuto "difficile",
je vais de toute façon adapter ce tuto à mon site, et non adapter mon site au tuto.
L'idée est de ne pratiquement rien changer de la partie en js,
mais je ne vais pas reprendre la partie suivante:
<?php
ini_set('session.use_only_cookies', true);
session_start();
header('Content-type: text/html; charset=UTF-8');
// Activer openssl si possible (sur le serveur) pour pouvoir se servir de la fonction 'openssl_random_pseudo_bytes'
function Unique_Sel()
{
return function_exists('openssl_random_pseudo_bytes')? hash("sha512",openssl_random_pseudo_bytes("128", $cstrong)) : hash("sha512",uniqid(rand(), true));
}
// On part du principe que le mot de passe est enregistrer comme ci-dessous en bdd (définir un passe) :
/*
$mot_de_passe = 'b';
// et rentrez ces valeurs en bdd (avec un login correspondant)
echo 'sel = '. $sel = Unique_Sel();
echo '<br>';
echo 'pass = '. hash("sha512",$mot_de_passe.$sel);
*/
// $_SESSION["login"] est définie uniquement si l'authentification est réussie. Si l'on est déjà enregistré normalement on fait un header de redirection vers une page souhaitée par exemple "menu.php"
if(isset($_SESSION["login"]))
{
// Désactivé pour l'exemple.
/*
header('Location: menu.php');
exit;
*/
}
// Teste les retours attendus du formulaire
$reponse = filter_input(INPUT_POST,'reponse', FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH);
$login = filter_input(INPUT_POST,'nom', FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW);
// Définition et récupération du sel aléatoire de session
$_SESSION["sel"] = isset($reponse,$_SESSION["sel"]) ? $_SESSION["sel"] : Unique_Sel();
// Message par défaut
$message = null;
// Initialisation des erreurs
$erreur = null;
// Si le formulaire est envoyé
if (isset($login,$reponse))
{
// Connexion à la base de donnée, ici test sur une bdd en local
// normalement on enregistre ces variables dans un fichier que l'on inclus avec un require
$hostname = "127.0.0.1";
$database = "nom_de_ma_base";
$username = "root";
$password = "";
try
{
// Configuration de PDO et connexion
// récupération mode objet
$pdo_options[PDO::ATTR_DEFAULT_FETCH_MODE] = PDO::FETCH_OBJ;
// mode d'erreurs
$pdo_options[PDO::ATTR_ERRMODE] = PDO::ERRMODE_EXCEPTION;
// Indispensable pour ne pas avoir execute qui formate en string avec un tableau en paramètre (incompatible avec la clause limit)
$pdo_options[PDO::ATTR_EMULATE_PREPARES] = false;
//charset reconnu dans la connexion à partir de php 5.3.6 auquel cas on peut supprimer l'option MYSQL_ATTR_INIT_COMMAND ci-dessous
//$pdo_options[PDO::MYSQL_ATTR_INIT_COMMAND] = "SET NAMES utf8";
$connexion = new PDO('mysql:host='.$hostname.';dbname='.$database.';charset=utf8', $username, $password, $pdo_options);
// Sélection du mot de passe correspondant au login (structure pour requête préparée)
$query = "SELECT pass FROM test WHERE login = ?";
// On prépare la requête
$stmt = $connexion->prepare($query);
// On lie la variable avec binParam
$stmt->bindParam(1, $login);
// Et on exécute
$stmt->execute();
// On récupère le tout dans un tableau (ce qui permet ici de compter facilement les résultats)
$result = $stmt->fetchAll();
if (empty($result))
{
// On sait ici que le login est non valide mais pas besoin de renseigner plus présicément les pirates
throw new Exception("Identifiants non valides");
}
// On ne devrait jamais rentrer dans la condition ci-dessous si la table est bien construite avec une clé primaire sur la colonne du login. Cela dit ce contrôle complémentaire ne coûte pas cher...
if (count($result) > 1)
{
throw new Exception("Erreur d'unicité du login");
}
// Récupération du pass (première ligne du tableau $result) qui est égal à : hash("sha512",mot_de_passe.sel_bdd);
$pass = $result[0]->pass;
// On reconstruit la même chaine que celle construite avec javascript pour renseigner le champ "reponse"
$compare_bdd = hash("sha512",$_SESSION["sel"].$pass);
// On compare les deux valeurs et si oui
if($reponse == $compare_bdd)
{
// On efface cette variable de session qui ne sert plus à rien
unset($_SESSION["sel"]);
// On régénère les identifiants de session
session_regenerate_id(true);
// Déclaration de la variable de session témoin de l'enregistrement
$_SESSION["login"] = 1;
// On envoie un message, sauf si on préfère la redirection mise en commentaire juste après
$message = "Vous êtes maintenant connecté !";
// Désactivé pour l'exemple. Normalement on fait un header de redirection vers une page souhaitée par exemple "menu.php"
/*
header('Location: menu.php');
exit;
*/
}
else
{
// On sait ici que le pass est non valide mais pas besoin de renseigner plus présicément les pirates
throw new Exception("Identifiants non valides");
}
}
catch (PDOException $e)// on récupère les erreurs PDO
{
// message en production
$erreur = "Erreur dans la requête d'authentification";
// message complet en développement -> !!!!!!! IMPORTANT mettre la ligne ci-dessous en commmentaires pour la production (pas besoin de renseigner plus précisément les pirates !!!!!!!!
$erreur .= ' : '.$e->getMessage();
}
catch (Exception $e)// puis on récupère les autres erreurs générées avec throw
{
$erreur = $e->getMessage();
}
$message = isset($erreur) ? $erreur : $message;
}
?>
J'ai déjà codé cette partie dans un fichier de traitement de formulaire,
et comme je suis très satisfait de cette partie de mon travail
je ne vais pas tout rechanger.
Je reprends également tel quel tout ton programme sur l'Ajax,
ce qui fait qu'au final je reprends pratiquement tout ce qu'il y a dans ton tuto.
Je risque d'avoir peut-être des soucis d'adaptation du tuto sur mon site,
mais ça je t'en parlerai après dans le cas seulement où je me retrouve complètement bloqué.
Il se peut donc, et je l'espère, que j'y arrive tout seul.