IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage PHP Discussion :

Vérification de la sécurisation d'un script de connexion et d'un script de requête


Sujet :

Langage PHP

  1. #1
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Octobre 2008
    Messages
    61
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2008
    Messages : 61
    Points : 36
    Points
    36
    Par défaut Vérification de la sécurisation d'un script de connexion et d'un script de requête
    Bonjour,

    Il y a longtemps j'avais programmé un site avec un système de compte avec une connexion via un formulaire (+ mémorisation dans une session et cookie).
    Je souhaiterai donc savoir si ces scripts sont sécurisés contre toutes attaques (XSS, injection SQL...).

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    // Connexion via formulaire -> AJAX
    $lg = $mysqli->real_escape_string(stripslashes($_POST["lg"]));
    $mp = $mysqli->real_escape_string(stripslashes($_POST["mp"]));
    $rm = $mysqli->real_escape_string(intval($_POST["rm"]));
     
    $r = $mysqli->query("SELECT id, mdp, cle, nom, prenom, valid, vcode FROM users WHERE email = '".$lg."' LIMIT 1");
    $nb = mysqli_num_rows($r);
    if(!empty($nb)) {
    	$v = mysqli_fetch_assoc($r);
    	$idu = intval($v['id']);
    	$mdp = $v['mdp'];
    	$cle = $v['cle']; // créée lors de la création du compte = hash de plusieurs valeurs
    	$nom = $v['nom'];
    	$prenom = $v['prenom'];
    	$valid = $v['valid'];
    	$vcode = $v['vcode'];
    	if(password_verify($mp, $mdp)) {
    		if($valid == '1') {
    			if(!isset($_SESSION["connexclt"])) $_SESSION["connexclt"] = array();
    			$_SESSION["connexclt"]['id'] = $idu;
    			$_SESSION["connexclt"]['login'] = $lg;
    			$_SESSION["connexclt"]['cle'] = $cle;
    			$_SESSION["connexclt"]['nom'] = $nom;
    			$_SESSION["connexclt"]['prenom'] = $prenom;
    			$mysqli->query("UPDATE users SET lastvisite = '".date('Y-m-d h:i:s')."' WHERE id = '".$idu."' LIMIT 1");
    			if($rm == 1) {
    				$k = password_hash($cle.$mdp, PASSWORD_HASH_ALGORITHM, array('cost' => PASSWORD_HASH_COST));
    				setcookie("nomcookie", $idu."[+]".$k, array('expires'=>time()+15778463,'path'=>'/','domain'=>$ssdomain.".".$domain,'secure'=>$domainsecure,'httponly'=>true,'samesite'=>"None"));	
    			}
    			return array('login'=>true,'error'=>false,'nom'=>$nom,'prenom'=>$prenom);
    		} else {
    			return array('login'=>false,'error'=>"NOVALID",'idu'=>$idu,'email'=>$lg,'nom'=>$nom,'prenom'=>$prenom,'vcode'=>$vcode);
     
    		}
    	} else {
    		$badlog = true;
    		$error = "Ces identifiants sont incorrects, veuillez vérifier votre email et votre mot de passe.";
    	}
    	//... traitement des erreurs de connexion ...
    }
     
    // Fonction de vérification de la connexion sur les pages privées
     
    function cltconnected() {
    	global $mysqli, $ssdomain, $domain, $domainsecure;
    	if(isset($_SESSION["connexclt"]) && isset($_SESSION["connexclt"]['id']) && isset($_SESSION["connexclt"]['login']) && isset($_SESSION["connexclt"]['cle']) && isset($_SESSION["connexclt"]['nom']) && isset($_SESSION["connexclt"]['prenom']) && $_SESSION["connexclt"]['id'] && $_SESSION["connexclt"]['login'] && $_SESSION["connexclt"]['cle'] && $_SESSION["connexclt"]['nom'] && $_SESSION["connexclt"]['prenom']) {
    		$id = intval($mysqli->real_escape_string($_SESSION["connexclt"]['id']));
    		$cle = $mysqli->real_escape_string($_SESSION["connexclt"]['cle']);
    		$r = $mysqli->query("SELECT email, valid FROM users WHERE id = '".$id."' AND cle = '".$cle."' LIMIT 1");
    		$nb = mysqli_num_rows($r);
    		if(!empty($nb)) {
    			$v = mysqli_fetch_assoc($r);
    			$email = $v['email'];
    			$valid = $v['valid'];
    			if($valid == '1') {
    				$vcle = // même hash de plusieurs valeurs - idem lors de la création du compte;
    				if($_SESSION["connexclt"]['cle'] == $vcle) {
    					$mysqli->query("UPDATE users SET lastvisite = '".date('Y-m-d h:i:s')."' WHERE id = '".$id."' LIMIT 1");
    					return true;
    				} else {
    					unset($_SESSION["connexclt"]);
    					if(isset($_COOKIE["nomcookie"])) setcookie("nomcookie", '', 0,'/',$ssdomain.".".$domain, $domainsecure, true);
    					return false;
    				}
    			} else {
    				unset($_SESSION["connexclt"]);
    				if(isset($_COOKIE["nomcookie"])) setcookie("nomcookie", '', 0,'/',$ssdomain.".".$domain, $domainsecure, true);
    				return false;
    			}
    		} else {
    			unset($_SESSION["connexclt"]);
    			if(isset($_COOKIE["nomcookie"])) setcookie("nomcookie", '', 0,'/',$ssdomain.".".$domain, $domainsecure, true);
    			return false;
    		}
     
    	} elseif(isset($_COOKIE["nomcookie"]) && $_COOKIE["nomcookie"]) {
    		$ccode = $_COOKIE["nomcookie"];
    		$tcode = explode("[+]",$ccode);
    		$idu = intval($mysqli->real_escape_string($tcode[0]));
    		$code = $tcode[1];
    		$r = $mysqli->query("SELECT email, mdp, cle, nom, prenom, valid FROM users WHERE id = '".$idu."' LIMIT 1");
    		$nb = mysqli_num_rows($r);
    		if(!empty($nb)) {
    			$v = mysqli_fetch_assoc($r);
    			$lg = $v['email'];
    			$mdp = $v['mdp'];
    			$cle = $v['cle'];
    			$nom = $v['nom'];
    			$prenom = $v['prenom'];
    			$valid = $v['valid'];
    			if($valid == '1') {
    				if(password_verify($cle.$mdp, $code)) {
    					if(!isset($_SESSION["connexclt"])) $_SESSION["connexclt"] = array();
    					$_SESSION["connexclt"]['id'] = $idu;
    					$_SESSION["connexclt"]['login'] = $lg;
    					$_SESSION["connexclt"]['cle'] = $cle;
    					$_SESSION["connexclt"]['nom'] = $nom;
    					$_SESSION["connexclt"]['prenom'] = $prenom;
    					$k = password_hash($cle.$mdp, PASSWORD_HASH_ALGORITHM, array('cost' => PASSWORD_HASH_COST));
    					setcookie("nomcookie", $idu."[+]".$k, array('expires'=>time()+15778463,'path'=>'/','domain'=>$ssdomain.".".$domain,'secure'=>$domainsecure,'httponly'=>true,'samesite'=>"Strict"));	
    					$mysqli->query("UPDATE users SET lastvisite = '".date('Y-m-d h:i:s')."' WHERE id = '".$idu."' LIMIT 1");
    					return true;
    				} else {
    					setcookie("nomcookie", '', 0,'/',$ssdomain.".".$domain, $domainsecure, true);
    					return false;
    				}
    			} else {
    				setcookie("nomcookie", '', 0,'/',$ssdomain.".".$domain, $domainsecure, true);
    				return false;
    			}
    		} else {
    			setcookie("nomcookie", '', 0,'/',$ssdomain.".".$domain, $domainsecure, true);
    			return false;
    		}
    	} else {
    		setcookie("nomcookie", '', 0,'/',$ssdomain.".".$domain, $domainsecure, true);
    		return false;
    	}
    }
    Et pour une simple requête (de recherche par exemple) j'utilise toujours soit intval quand c'est un entier soit $mysqli->real_escape_string.
    Les valeurs dans la requête sont toujours entourées par des apostrophes.
    Apparemment "real_escape_string" ne serait pas suffisant (ex. avec "1 OR 1 = 1") mais ça c'est si on ne met pas les valeurs entre apostrophes, non ?

    Exemple de requête basique :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    $val = $mysqli->real_escape_string(stripslashes($_POST["val"]));
    $r = $mysqli->query("SELECT param FROM table WHERE param = '".addslashes($val)."'");
    J'ai vu que maintenant il est conseillé d'utiliser des requêtes préparées mais tout reprendre serait très long, donc je souhaiterai savoir si ces méthodes sont entièrement sécurisées ou non.

    En vous remerciant par avance !

  2. #2
    Membre éclairé
    Homme Profil pro
    Urbaniste
    Inscrit en
    Août 2023
    Messages
    386
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Autre

    Informations professionnelles :
    Activité : Urbaniste

    Informations forums :
    Inscription : Août 2023
    Messages : 386
    Points : 795
    Points
    795
    Par défaut
    Bonjour,

    Non ce n'est pas terrible.

    https://cheatsheetseries.owasp.org/c...eat_Sheet.html

    Il y a tout un tas de choses que vous pourriez faire,
    il faudrait commencer par s'interroger de ce qui vaut le coup d'être
    implémenté en regard du risque.

    nonobstant le fait que "très long" ça ne veut rien dire,
    c'est combien "très long", 1 heure, 1 jour, 1 semaine, 1 an ?
    ça vous coûte quoi si votre bidule se fait pirater ?
    ça coule la boîte ?

    Bonne journée.

  3. #3
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Octobre 2008
    Messages
    61
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2008
    Messages : 61
    Points : 36
    Points
    36
    Par défaut
    Merci pour votre avis.

    Oublions l'aspect temps à y passer.
    Si je veux que ce soit sécurisé (aucune attaque possible), que dois-je faire en réalité ?
    Passer toutes les requêtes sensibles en requêtes préparées et ajouter par exemple "strip_tags" (pour les attaques XSS) lors de la récupération des valeurs saisies par le visiteur, ce serait suffisant ?

  4. #4
    Membre éclairé
    Homme Profil pro
    Urbaniste
    Inscrit en
    Août 2023
    Messages
    386
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Autre

    Informations professionnelles :
    Activité : Urbaniste

    Informations forums :
    Inscription : Août 2023
    Messages : 386
    Points : 795
    Points
    795
    Par défaut
    Bonjour,

    Pour qu'aucune attaque ne soit possible il faut implémenter
    l'ensemble des recommandations OWASP.
    Le faire correctement.
    Et, espérer qu'on ne soit pas sujet à une attaque
    dans le scope de l'infrastructure logiciel d'hébergement.
    Faut aussi former ces utilisateurs pour qu'ils ne se fassent pas
    hameçonner.
    On peut aller loin comme ça.

    Sinon, oui côté serveur ce serait mieux d'implémenter
    ce que vous mentionnez.
    Côté vue faudrait html_encodé tout ce qui sort de la base de données.

    Ce serait pas mal d'utiliser un certificat SSL.

    La reconnexion automatique à base de cookie, c'est sensible, surtout sur +100 jours.
    Ce serait ben de mixer l'IP source, ou la signature du navigateur.

    On pourrait mettre du CAPTCHA, du throttling, des CSRF, vérifier les headers, des cookies chiffrés,
    et tout un tas d'autres choses....

    Je me répète, tout cela est à calibrer en fonction du risque réel.

    Vous allez subir un test de sécurité ? Le sujet est de ne pas apparaître comme un abruti aux yeux du client ?

    Bonne journée.

  5. #5
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Octobre 2008
    Messages
    61
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2008
    Messages : 61
    Points : 36
    Points
    36
    Par défaut
    Bonjour et merci unanonyme pour toutes ces infos.

    Pour répondre aux questions :
    Non le site ne subira pas de test de sécurité mais certaines infos sont sensibles donc il me doit de les protéger correctement.

    Je commence à faire mes modifications, par les simples requêtes d'update pour le moment.
    Mais pour être sûr que ce que je fais sert à quelque chose, je me permet de vous demander si ça convient.

    Déjà j'avais oublié de préciser que mes pages appelées en AJAX commencent toutes par :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    if(str_replace($ssdomain.".","",$_SERVER['SERVER_NAME']) != $domain) {
    	//Renvoi une erreur
    	exit;
    }
    Ça vérifie déjà que la page est appelée uniquement par le site.

    Et voici comment je compte sécuriser un simple appel pour modifier une valeur dans une table :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    $id = intval($_POST['id']);
    $value = strip_tags(stripslashes($_POST['value']));
     
    $p = $mysqli->prepare("UPDATE table SET val = ? WHERE id = '".$id."' LIMIT 1");
    $r = $p->execute(array($value));
    Suis-je sur la bonne voie et est-ce suffisant ?

    Encore merci !

  6. #6
    Expert confirmé
    Avatar de laurentSc
    Homme Profil pro
    Webmaster débutant perpétuel !
    Inscrit en
    Octobre 2006
    Messages
    10 430
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : France, Isère (Rhône Alpes)

    Informations professionnelles :
    Activité : Webmaster débutant perpétuel !
    Secteur : Industrie

    Informations forums :
    Inscription : Octobre 2006
    Messages : 10 430
    Points : 5 784
    Points
    5 784
    Billets dans le blog
    1
    Par défaut
    Bonjour,
    je lis cette discussion par hasard et ne sais s'il est trop tard pour intervenir. Je ne suis pas un expert, même si mon statut le laisse entendre, mais pour gérer une bdd en mysql, je fais confiance à l'excellente classe https://github.com/rawsrc/PDOPlusPlus (EN et à jour) ou https://www.developpez.net/forums/bl...dutiliser-pdo/ (FR mais pas mis à jour depuis 2020...). L'auteur est excellent.
    Il vaut mieux viser la perfection et la manquer que viser l'imperfection et l'atteindre. - Bertrand Russell

    Si la discussion est résolue, merci de cliquer sur le bouton

Discussions similaires

  1. votre avis sur la sécurisation de ce script
    Par michel71 dans le forum PHP & Base de données
    Réponses: 2
    Dernier message: 25/10/2022, 11h29
  2. MySql et script TCL : requête sql update
    Par ckefran9 dans le forum Tcl/Tk
    Réponses: 11
    Dernier message: 21/07/2010, 11h01
  3. Script de requêtes HTTP avec paramètre en boucle
    Par padsalad dans le forum Langage
    Réponses: 3
    Dernier message: 30/06/2009, 16h46
  4. Script de requêtes HTTP avec paramètre en boucle
    Par padsalad dans le forum Autres
    Réponses: 0
    Dernier message: 29/06/2009, 11h32
  5. script génére requête mysql
    Par Invité dans le forum Requêtes
    Réponses: 1
    Dernier message: 30/06/2007, 23h58

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo