[RESOLU] Protection formulaires.

Eléphant du PHP | 290 Messages

01 nov. 2014, 00:05

Merci pour tout :D
Je te rassure, j'ai maintenant très bien compris la différence entre l'utilité de mysqli_real_escape_string
et l'utilité de htmlentities grâce à toi et à ynx.
Je vous remercie beaucoup.
Je vais maintenant essayé de faire mon SHA-256 et peut-être aussi le grain de sable
(je ne sais pas du tout ce que c'est mais bon je verrai).

Eléphant du PHP | 290 Messages

04 nov. 2014, 11:55

Bonjour,
Je travaille toujours sur le hashage et le grain de sable :D

J'aurais par ailleurs d'autres questions sur la protection/sécurité:

1) A propos de l'affichage avec htmlentities, à chaque fois que je veux permettre
dans mon formulaire l'affichage d'une valeur postée, j'utilise htmlentities pour me prémunir contre les injections xss.
Question peut-être bête, mais c'est pour mettre les choses bien au clair, je n'ai pas besoin d'utiliser htmlentities
quand cet affichage concerne la valeur d'une case d'options, d'une case à cocher ou d'une liste déroulante?
=> car l'utilisateur ne peut rien écrire.

2) Dans le cas d'une liste déroulante dans un formulaire, par exemple:
<select name="animal_favori" size="1" >
     <option value="chien" selected="selected"> chien</option>
     <option value="chat"> chat</option>
     <option value="hamster"> hamster</option>
     <option value="lapin"> lapin</option>
     <option value="tortue"> tortue</option>
</select>
Je veux permettre l'affichage d'une valeur postée.
J'écris:
<select name="animal_favori" size="1" >
     <option value="<?php if(isset($_POST['animal_favori'])) { echo htmlentities($_POST['animal_favori'],ENT_QUOTES, 'UTF-8');}; ?>" selected="selected"> chien</option>
     <option value="<?php if(isset($_POST['animal_favori'])) { echo htmlentities($_POST['animal_favori'],ENT_QUOTES, 'UTF-8');}; ?>"> chat</option>
     <option value="<?php if(isset($_POST['animal_favori'])) { echo htmlentities($_POST['animal_favori'],ENT_QUOTES, 'UTF-8');}; ?>"> hamster</option>
     <option value="<?php if(isset($_POST['animal_favori'])) { echo htmlentities($_POST['animal_favori'],ENT_QUOTES, 'UTF-8');}; ?>"> lapin</option>
     <option value="t<?php if(isset($_POST['animal_favori'])) { echo htmlentities($_POST['animal_favori'],ENT_QUOTES, 'UTF-8');}; ?>"> tortue</option>
</select>
Mais ça ne va pas car je n'ai plus les valeurs chien, chat, hamster, lapin et tortue dans mon attribut value="..."

a) Faut-il donc que j'écrive le code suivant:
<select name="animal_favori" size="1" >
     <option value="<?php if(isset($_POST['animal_favori'])) { echo htmlentities('chien',ENT_QUOTES, 'UTF-8');}; ?>" selected="selected"> chien</option>
     <option value="<?php if(isset($_POST['animal_favori'])) { echo htmlentities('chat',ENT_QUOTES, 'UTF-8');}; ?>"> chat</option>
     <option value="<?php if(isset($_POST['animal_favori'])) { echo htmlentities('hamster',ENT_QUOTES, 'UTF-8');}; ?>"> hamster</option>
     <option value="<?php if(isset($_POST['animal_favori'])) { echo htmlentities('lapin',ENT_QUOTES, 'UTF-8');}; ?>"> lapin</option>
     <option value="t<?php if(isset($_POST['animal_favori'])) { echo htmlentities('tortue',ENT_QUOTES, 'UTF-8');}; ?>"> tortue</option>
</select>
?
Si oui, alors la fonction htmlentities a-t-elle du sens ici? => je crois qu'elle ne sert strictement à rien.
Comment verriez-vous le code?

b)question esthétique (moins en rapport avec mon message mais c'est pour moi le bon moment de la poser si vous êtes d'accord :priere: )
le nom des animaux s'affiche dans les cases, mais si je veux, pour une raison esthétique, mettre des espaces à gauche ou à droite de chaque nom d'animal,
je peux utiliser l'espace insécable &nbsp; autant que besoin mais ça va vite devenir laid, sinon j'utilise des paramètres comme top ou left
mais il faudra à chaque fois que je vérifie le rendu à l'écran pour chaque champs si je fais comme ça pour tout mon formulaire et le pire c'est qu'il
faudra que je change toutes mes mesures si j'ajoute un élément nouveau dans ma page qui décale donc les autres éléments de ma page
(je pense en particulier aux mesures du paramètre top à revoir pour tous les champs pour chaque ajout d'un élément nouveau).
Comment vous y prendriez-vous?
Que pensez-vous des styles en lignes pour éviter d'avoir des références à d'autres endroits? (question moins importante mais j'aimerais bien savoir quand même ce que vous en pensez)

3) Pour la protection de mon site, j'ajoute du code contre les injections xss et sql, j'utilise un captcha questions/réponses et maintenant je travaille sur le hashage et le grain de sable.
J'aurais néanmoins une question.
Malgré tout ça, est-ce qu'un pirate peut attaquer le site par les barres du navigateur: barre d'adresse et de moteur de recherche?
Je n'ai mis aucun code pour me protéger à ces niveaux là.
Pour info, mais je ne sais pas si ce détail a son importance, je n'ai dans mon site aucun formulaire conçu avec la méthode GET.

ViPHP
xTG
ViPHP | 7331 Messages

04 nov. 2014, 14:08

J'ai perdu mon message... Merci le proxy d'entreprise... :evil:

Bon plus trop le temps de réécrire mon pavé.
En bref :

2) ce que tu fais n'a aucun sens, le but est de sélectionner l'option en fonction de ce qui a été posté.
A savoir donc :
<select name="test">
<option value="1" <?php if( $_POST['test'] == 1 ) echo 'selected="selected"'; ?>>1</option>
b) htmlentities a du sens à chaque echo affichant une variable.

3) tu peux à la rigueur rajouter des tests basique sur le type de données passées dans l'url.
Si tu attends un entier il ne faut pas que cela soit une chaîne de caractères, ect.
Et ne surtout jamais utiliser la fonction extract() pour récupérer facilement les paramètres de l'url ou d'un formulaire.

Eléphant du PHP | 290 Messages

04 nov. 2014, 16:38

Merci pour ta réponse.

1. J'ai quelques questions à propos du code que tu as écris.
Tu as écris dans ta réponse que htmlentities a du sens à chaque echo affichant une variable,
mais tu n'as pas utilisé htmlentities dans ton exemple:
<select name="test">
 <option value="1" <?php if( $_POST['test'] == 1 ) echo 'selected="selected"'; ?>>1</option>
Pourtant la variable se trouve dans 'selected="selected"'.
a) Faut-il donc ajouter dans ton code quelque chose comme ci-dessous?
<select name="test">
 <option value="1" <?php if( $_POST['test'] == 1 ) echo htmlentities('"selected="selected"',ENT_QUOTES, 'UTF-8');
Mais écrit comme ça il y a peut-être un problème de syntaxe car '"selected="selected"' fait référence à une variable,
ce qui diffère d'annoncer directement une variable en commençant par le signe $.
b) D'après ton code, je vais retrouver '"selected="selected"' à toutes les lignes.
Comment j'insère donc ma valeur par défaut que va voir l'utilisateur la première fois qu'il verra le formulaire?
c) autre chose, pourquoi n'utilises-tu pas les accolades dans ton if dans ton exemple?
<select name="test">
<option value="1" <?php if( $_POST['test'] == 1 ) { echo 'selected="selected"';}; ?>>1</option>
Les accolades ne sont-elles pas obligatoires avec un if pour exprimer la conséquence, le "dans ce cas..."?
d) quand tu dis que ce que je fais avec isset n'a pas de sens, tu veux dire que ça n'a pas de sens lorsqu'il s'agit de cases d'options,
de cases à cocher et de listes déroulantes ou que ça n'a pas de sens tout court, quel que soit le type d'input?

2. A propos d'extract.
J'utilise parfois extract dans des feuilles de traitement de formulaire comme dans:
$chart = mysqli_fetch_assoc($query);
extract($chart);
Je ressors ainsi des données de ma bdd.
Pourquoi c'est si déconseillé et vers quel type de fonction vous me conseilleriez-vous donc de me retourner
en remplacement de ça?

3. Ma question sur la sécurité est très générale: pensez-vous que c'est suffisant d'avoir
du code contre les injections xss et sql, un captcha questions/réponses pour tout entrée sur une session et du code de hashage + grain de sable
pour toute entrée de mot de passe.
Est-ce que vous vous feriez quelque chose de plus contre d'éventuelles attaques depuis les barres du navigateur: barre d'adresse et de moteur de recherche,
ou pas?

ViPHP
xTG
ViPHP | 7331 Messages

04 nov. 2014, 18:17

Et seconde fois que je me fais avoir par le proxy...
Bon je reprend...

1) aucune variable dans le echo. Ce n'est qu'une chaîne de caractère fixe. Elle ne pourra jamais varier sauf si quelqu'un édite le fichier.

b) Il faut modifier le if pour qu'il corresponde à la value de l'option. ;)
<option value="1" <?php if( $_POST['test'] == 1 ) { echo 'selected="selected"';}; ?>>1</option>
<option value="2" <?php if( $_POST['test'] == 2 ) { echo 'selected="selected"';}; ?>>2</option>
La valeur par défaut sera dans ce cas là toujours la première valeur.
Si tu souhaites une autre valeur il faut modifier la condition pour y introduire une seconde variable contenant la valeur par défaut.

c) La traduction de :
if( condition )
action1
action2
est :
if( condition )
{
action1
}
action2
C'est applicable aussi à : foreach, for, while, ect

d) c'est l'affichage suite à la condition qui n'avait pas de sens, il faut dans ton cas utiliser selected comme dans mon exemple.

2)
Exemple qui fout la zone :
$debug = 32;
$sql = 'SELECT debug FROM maTable';
$query = mysqli_query($sql);
$row = mysqli_fetch_array($query);
extract($row);
Combien vaut $debug à la fin de ce script ?
Il vaut la valeur qu'il y a dans la table... C'est moche hein ! ;)
En remplacement il n'y a rien de mieux qu'appeler un chat un chat.
Donc utiliser $row['debug'] car on sait ce qu'on appelle.

3) je ne vois rien de plus.

ViPHP
AB
ViPHP | 5818 Messages

04 nov. 2014, 19:15

Salut,

Il faut éviter de faire transférer les mots de passe en clair dans les tuyaux. Donc soit tu utilise une connexion cryptée type ssl sinon tu sel+hash en javascript (rien n'empêche d'utiliser les deux techniques simultanément). J'ai retrouvé un exemple complet (qui utilise mysqli) :
<?php
header('Content-type: text/html; charset=UTF-8');
 
if (!session_id()) session_start();	
 
// Définition de variables par défaut
$_SESSION["sel"] = isset($_POST['reponse'],$_SESSION["sel"]) ? $_SESSION["sel"] : hash("sha256",(uniqid(rand(), true)));
 
$message = null;
 
 
 
// Si l'on est déjà enregistré
if(isset($_SESSION["login"])) 
{
	// Désactivé pour l'exemple. Normalement on fait un header de redirection vers une page souhaitée par ex menu
	/* 
		header('Location: menu.php');
		exit;
	*/
}
 
 
 
 
// Si le formulaire est envoyé						  
if (isset($_POST['reponse'],$_POST['utilisateur']))
{
 
	// ici test sur une bdd en local
	$hostname = "localhost";
	$database = "mabase";
	$username = "root";
	$password = "";
 
	 // Connexion à la base
	$connection = mysqli_connect($hostname, $username, $password,$database);
 
 
	// Sélection du mot de passe correspondant au login (structure pour requête préparée)
	$query = "SELECT pass, pseudo  FROM membres WHERE pseudo = ?";
 
	// Préparation de la requête
	if($stmt = mysqli_prepare($connection, $query)) 
	{
		// On lie la variable en indiquant que c'est une chaine de caractères
		mysqli_stmt_bind_param($stmt,"s",$login);
 
		$login = filter_input(INPUT_POST, 'utilisateur', FILTER_SANITIZE_STRING);
 
		// On exécute la requête
		mysqli_stmt_execute($stmt);
 
		// Stockage du résultat pour utiliser mysqli_stmt_num_rows dans les meilleures conditions
		mysqli_stmt_store_result($stmt);	
 
		// Tant qu'on y est on bind (associe) aussi le résultat
		mysqli_stmt_bind_result($stmt, $motdepasse, $identifiant);
 
		// On compte le résultat
		$nb_res = mysqli_stmt_num_rows($stmt);
 
		if($nb_res > 0)
			{
				if($nb_res == 1)
				{
					// Récupération des valeurs. Normalement on fait un while mais ici il n'y a qu'un résultat...
					mysqli_stmt_fetch($stmt);
 
					// $motdepasse et $identifiant sont les valeurs définies par mysqli_stmt_bind_result
					$mpd = $motdepasse;
					$login = $identifiant;
 
					// Contrôle devant correspondre à la méthode de hashage javascript
					$chaine = $login.$_SESSION["sel"].$mpd;
					$hash = hash("sha256",$chaine);
 
					if ($hash == $_POST['reponse'])
					{
						// On redémarre une autre session et l'on efface la précédente par sécurité (à faire uniquement si l'éventuelle session en cours peut être effacée).
						session_regenerate_id(true);
						$_SESSION = array();
 
						$message = "Vous êtes maintenant connecté";
 
						// déclaration de l'identifiant de session (on attribue souvent le login)
						$_SESSION["login"] = 1;	
 
						// Désactivé pour l'exemple. Normalement on fait un header de redirection vers une page souhaitée par ex menu
						/* 
							header('Location: menu.php');
							exit;
						*/	
					}
					else
					{
						// On sait ici que c'est le mot de passe qui est incorrect mais pas la peine de donner des infos précises qui pourraient aider le pirate
						$message = "login ou mot de passe incorrect";
					}
				}
				else if($nb_res > 1) 
				{
					$message = "Il existe plus d'un utilisateur ayant ce même login (pb dans la conception de la base de donnée, login devrait être unique)";
				}
			}
			else 
			{ 
				// On sait ici que c'est le pseudo qui est incorrect mais pas la peine de donner des infos précises qui pourraient aider le pirate
				$message = "login ou mot de passe incorrect";
			}
 
		// Libère le résultat 
    	        mysqli_stmt_free_result($stmt);
 
		// Fermeture du traitement 
   		mysqli_stmt_close($stmt);
	}
	else 
	{
		$message = 'problème dans la requête';
	}
 
	// On régénère le sel
	$_SESSION["sel"] = hash("sha256",(uniqid(rand(), true)));
}
?>
<!DOCTYPE html>
<html lang="fr">
 
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
 
<title>Identification avec mot de passe hashé et grain de sel aléatoire</title>
 
 
<script type="text/javascript">
<!--
function SHA256(s){
 
	var chrsz   = 8;
	var hexcase = 0;
 
	function safe_add (x, y) {
		var lsw = (x & 0xFFFF) + (y & 0xFFFF);
		var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
		return (msw << 16) | (lsw & 0xFFFF);
	}
 
	function S (X, n) { return ( X >>> n ) | (X << (32 - n)); }
	function R (X, n) { return ( X >>> n ); }
	function Ch(x, y, z) { return ((x & y) ^ ((~x) & z)); }
	function Maj(x, y, z) { return ((x & y) ^ (x & z) ^ (y & z)); }
	function Sigma0256(x) { return (S(x, 2) ^ S(x, 13) ^ S(x, 22)); }
	function Sigma1256(x) { return (S(x, 6) ^ S(x, 11) ^ S(x, 25)); }
	function Gamma0256(x) { return (S(x, 7) ^ S(x, 18) ^ R(x, 3)); }
	function Gamma1256(x) { return (S(x, 17) ^ S(x, 19) ^ R(x, 10)); }
 
	function core_sha256 (m, l) {
		var K = new Array(0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5, 0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5, 0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3, 0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174, 0xE49B69C1, 0xEFBE4786, 0xFC19DC6, 0x240CA1CC, 0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA, 0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7, 0xC6E00BF3, 0xD5A79147, 0x6CA6351, 0x14292967, 0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13, 0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85, 0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3, 0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070, 0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5, 0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3, 0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208, 0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2);
		var HASH = new Array(0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19);
		var W = new Array(64);
		var a, b, c, d, e, f, g, h, i, j;
		var T1, T2;
 
		m[l >> 5] |= 0x80 << (24 - l % 32);
		m[((l + 64 >> 9) << 4) + 15] = l;
 
		for ( var i = 0; i<m.length; i+=16 ) {
			a = HASH[0];
			b = HASH[1];
			c = HASH[2];
			d = HASH[3];
			e = HASH[4];
			f = HASH[5];
			g = HASH[6];
			h = HASH[7];
 
			for ( var j = 0; j<64; j++) {
				if (j < 16) W[j] = m[j + i];
				else W[j] = safe_add(safe_add(safe_add(Gamma1256(W[j - 2]), W[j - 7]), Gamma0256(W[j - 15])), W[j - 16]);
 
				T1 = safe_add(safe_add(safe_add(safe_add(h, Sigma1256(e)), Ch(e, f, g)), K[j]), W[j]);
				T2 = safe_add(Sigma0256(a), Maj(a, b, c));
 
				h = g;
				g = f;
				f = e;
				e = safe_add(d, T1);
				d = c;
				c = b;
				b = a;
				a = safe_add(T1, T2);
			}
 
			HASH[0] = safe_add(a, HASH[0]);
			HASH[1] = safe_add(b, HASH[1]);
			HASH[2] = safe_add(c, HASH[2]);
			HASH[3] = safe_add(d, HASH[3]);
			HASH[4] = safe_add(e, HASH[4]);
			HASH[5] = safe_add(f, HASH[5]);
			HASH[6] = safe_add(g, HASH[6]);
			HASH[7] = safe_add(h, HASH[7]);
		}
		return HASH;
	}
 
	function str2binb (str) {
		var bin = Array();
		var mask = (1 << chrsz) - 1;
		for(var i = 0; i < str.length * chrsz; i += chrsz) {
			bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (24 - i%32);
		}
		return bin;
	}
 
	function Utf8Encode(string) {
		string = string.replace(/\r\n/g,"\n");
		var utftext = "";
 
		for (var n = 0; n < string.length; n++) {
 
			var c = string.charCodeAt(n);
 
			if (c < 128) {
				utftext += String.fromCharCode(c);
			}
			else if((c > 127) && (c < 2048)) {
				utftext += String.fromCharCode((c >> 6) | 192);
				utftext += String.fromCharCode((c & 63) | 128);
			}
			else {
				utftext += String.fromCharCode((c >> 12) | 224);
				utftext += String.fromCharCode(((c >> 6) & 63) | 128);
				utftext += String.fromCharCode((c & 63) | 128);
			}
 
		}
 
		return utftext;
	}
 
	function binb2hex (binarray) {
		var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
		var str = "";
		for(var i = 0; i < binarray.length * 4; i++) {
			str += hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8+4)) & 0xF) +
			hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8  )) & 0xF);
		}
		return str;
	}
 
	s = Utf8Encode(s);
	return binb2hex(core_sha256(str2binb(s), s.length * chrsz));
}
// -->
</script>
 
 
<script language="javascript">
<!--
  function doChallengeResponse(form) {
 
    var str = form.utilisateur.value+'<?php echo $_SESSION["sel"]?>'+form.mot_de_passe.value;
    form.reponse.value = SHA256(str);
    form.mot_de_passe.value = "";
 
	// 64 correspond à la longueur d'un sha 256 (condition à modifier si l'on change d'encodage)
	if (form.mot_de_passe.value == '' && form.reponse.value.length == 64)
	{
		form.submit();
	} 
	else return false;	
 
  }
// -->
</script>
 
</head>
 
<body>
    <div> 
        <form name="identification" method="post" action="#">
            <p>
                Utilisateur:
                <input type="text" name="utilisateur" size="32" maxlength="32"><br>
                Mot de passe:
                <input type="password" name="mot_de_passe" size="32" maxlength="32"><br>
                <input onclick="doChallengeResponse(this.form); return false;" type="button" value="identification">
                <input type="hidden" name="reponse"  value="">
           </p>
        </form>
 
        <p>
        <?php if(isset($message)) echo $message;?>
        </p>
 
        <noscript>
        Vous devez activer javascript pour la connexion.
        </noscript>
    </div> 
</body>
</html>

Eléphant du PHP | 290 Messages

04 nov. 2014, 21:19

Merci xTG pour la réponse à toutes mes questions :D

Il n'y a plus qu'un truc pas très clair pour moi, c'est en ce qui concerne extract
Il vaut la valeur qu'il y a dans la table...
en parlant de la valeur d'une variable sortie d'une table avec la fonction extract();
Mais c'est pourtant bien le résultat que je recherche avec extract:
sortir une variable de ma table pour en faire ce que je veux.
Donc la raison pour laquelle il n'est pas apprécié c'est pourtant la raison pour laquelle je veux l'utiliser.
Je ne comprend donc pas ce qu'elle a de mal cette fonction.

Concernant le hashage et le grain de sable, je te remercie beaucoup AB.
Je vais voir ça dans un deuxième temps pour une question d'organisation mais je garde
ton message précieusement sous le coude.

Pour ce qui est de la ligne de code:
<select name="test">
  <option value="1" <?php if( $_POST['test'] == 1 ) echo 'selected="selected"'; ?>>1</option>
Mon navigateur me retourne index indéfini sur la variable $test,
et pour régler la chose j'ai écrit:
<select name="test">
  <option value="1" <?php if(isset($_POST['test']) && $_POST['test'] == 1 ) echo 'selected="selected"'; ?>>1</option>
Ai-je bien fait?
Je dis ainsi au navigateur de faire telle chose si la variable existe afin qu'il ne soit pas en train de cherche des index s'il n'y en a pas.
Le raisonnement est bon?

J'en profite aussi pour dire que dans mon attribut valeur pour un champs text j'écris
value="<?php if(isset($_POST['test'])) { echo htmlentities($_POST['test'], ENT_QUOTES, 'UTF-8');}; ?>"

pour préciser que, malheureusement, le réaffichage de mes données ne se fait pas dans un cas comme dans l'autre.

Mais je me demande si ça vient pas de ma page de traitement de formulaire.
Dans cette page, en premier lieu je déclare mes valeurs postées.
$test_poste1=$_POST['test1'];
$test_poste2=$_POST['test2'];
$test_poste3=$_POST['test3'];
suivi de ma boîte js s'il manque le remplissage de certaines variables:
if (empty($test_poste1) || empty($test_poste2) || empty($test_poste3)){
?>
[javascript]
<script type="text/javascript">
alert('Veuillez saisir les données manquantes');
document.location.href="http://localhost/mon_site/ma_page.php"
</script>
[/javascript]
<?php
exit();
}
Je me demande en fait si cet n'est pas la fonction exit(); qui empêche le réaffichage car elle est censée arrêter la lecture de la page
de traitement de formulaire.
Les variables postées déclarées juste avant auraient-t-elles été effacées avec le exit?
Mais si oui, comment est-ce que je pourrais faire car avec le exit(); je suis sur
que les informations incomplètes ne rentrent pas dans ma bdd, ce qui est une bonne chose?
A moins que le soucis vienne de ma page de formulaire...

ViPHP
xTG
ViPHP | 7331 Messages

05 nov. 2014, 14:03

Allez je me remet à la rédaction après avoir manger en espérant que le proxy ne mange pas encore mon message... :twisted:
Je ne comprend donc pas ce qu'elle a de mal cette fonction.
Je ne dis pas que tu n'as pas à l'utiliser. Je te dis juste que c'est fortement déconseillé du fait de son comportement.

Exemple bénin :
<?php
$fichier = 'questionnaire.txt';

extract($_GET);
if( $action == "poster_reponse" )
{
   //...
}
else if( $action == "afficher_questionnaire" )
{
   $questionnaire = file_get_contents($fichier);
   echo $questionnaire; // on affiche le questionnaire
}
Maintenant si on prend un étudiant un peu futé qui a pas appris son cours il va appeler le script comme ceci : script.php?action=afficher_questionnaire&fichier=reponses_questionnaire.txt
Ou bien si on prend quelqu'un qui veut accéder à des fichiers sensibles : script.php?action=afficher_questionnaire&fichier=/dossier_systeme/fichier_sensible
(le / indiquant la racine du système de fichier unix)

extract() peut être utilisé, mais avec de très grandes précautions... C'est souvent à cause de l'oubli de l'utilisation de cette fonction qu'on créé inconsciemment des failles de sécurité.
Ai-je bien fait?
Je dis ainsi au navigateur de faire telle chose si la variable existe afin qu'il ne soit pas en train de cherche des index s'il n'y en a pas.
Le raisonnement est bon?
Mon exemple était en effet simplifié, tu as bien fait.
Je me demande en fait si cet n'est pas la fonction exit(); qui empêche le réaffichage car elle est censée arrêter la lecture de la page
de traitement de formulaire.
Après un exit() plus rien n'est exécuté.

ViPHP
AB
ViPHP | 5818 Messages

05 nov. 2014, 18:33

Salut,

Concernant extract(), xTG t'a montré des exemples qui posent des problèmes de sécurité
extract() peut être utilisé, mais avec de très grandes précautions...
Pour résumé, l'avertissement du manuel est très éloquent
Avertissement
N'utilisez pas extract() sur des données inconnues, comme les données utilisateurs (i.e. $_GET, $_FILES, etc.).
on peut donc rajouter à la liste $_POST et $_COOKIE

Eléphant du PHP | 290 Messages

05 nov. 2014, 19:10

Merci pour tes explications.

Il a y deux points pour lesquels j'aurais besoin d'aide.
Ce serait sympa si tu peux m'aider à propos de ces deux points :D

1) Concernant extract, concrètement j'utilise cette fonction pour ressortir
le mot de passe et le nom d'utilisateur de la personne en train de s'identifier sur le site
dans le base de données.
Je te montre mon code c'est beaucoup plus simple pour comprendre:
$req="SELECT nomutilisateur,motdepasse FROM identification";
 
$query=mysqli_query($connexion,$req) or die ("Exécution de la requête impossible: ".mysqli_error($connexion));

$chart = mysqli_fetch_assoc($query);
extract($chart);

//teste si le nom d'utilisateur et le mot de passe sont bons tous les deux
if ($nomutilisateur_poste == $nomutilisateur && $motdepasse_poste == $motdepasse){
Je pense maintenant que tu comprends parfaitement ce que je veux faire.
Je crois comprendre que le point dangereux avec extract est qu'il extrait toutes les variables,
y compris des variables dont on a pas forcément besoin dans la suite du code et qui peuvent faire des dégâts
car potentiellement dangereuses.
Donc tu dis qu'il est préférable d'extraire uniquement les variable dont on a besoin, une par une, et laisser
les autres où elles sont.
C'est ça?
Dans le cas ci-dessus, serait-il donc préférable de sortir que le mot de passe et le login de l'utilisateur connecté
plutôt que ceux de tous?
Pour dire ça autrement, y aurait-il un risque à sortir les noms d'utilisateurs et mots de passe autres que ceux de
l'utilisateur connecté car ces derniers peuvent présenter un risque?
Si la réponse est oui, je suppose que dans ma ligne
$req="SELECT nomutilisateur,motdepasse FROM identification";
il faut donc que je rajoute une clause WHERE pour indiquer le nom de l'utilisateur connecté,
ainsi la fonction extract plus bas ne présente plus de risque.
Mais je ne peux pas écrire:
$req="SELECT nomutilisateur,motdepasse FROM identification WHERE nomutilisateur= DUPOND";
car je ne sais pas si c'est Mr Dupond ou quelqu'un d'autre qui est connecté.
Est-ce que tu comprends?
Qu'est que tu en penses?

2) A propos de la fonction exit() tu dis:
Après un exit() plus rien n'est exécuté.

Donc ça veut dire que ce qu'il y a avant l'exit() est bien exécuté, c'est bien ça?
Avant l'exit() j'ai la déclaration de mes valeurs postées.
Donc pourquoi ne seraient-elle pas réaffichées?
Si l'exit() fait que les variables postées déclarées ne sont plus prises en compte,
par quel moyen pourrais-je assurer mon réaffichage sans condamner ma fonction exit()
qui a comme utilité d'éviter d'entrer des données incomplètes dans ma base de données?

Eléphant du PHP | 290 Messages

05 nov. 2014, 19:13

J'avais déjà lu plusieurs fois le lien envoyé par AB, que je remercie d'ailleurs.
Mais j'ai du mal à comprendre :cry:

ViPHP
xTG
ViPHP | 7331 Messages

05 nov. 2014, 20:58

$req="SELECT motdepasse FROM identification WHERE nomutilisateur= " . mysqli_real_escape_string($nomutilisateur_poste);
Tu n'as plus qu'à vérifier que le mot de passe est correct. ;)
A propos de la fonction exit()
Rien de compliqué.
Tout ce qui est avant exit() est exécuté.
Tout ce qui est après exit() n'est pas exécuté.

ViPHP
AB
ViPHP | 5818 Messages

05 nov. 2014, 22:55

Concernant l'inscription et la connexion dans un espace membre qui semble te poser problème, j'ai fait un tuto ici.
C'est la base que l'on peux ensuite compléter avec un hash côté visiteur quand on n'utilise pas de connexion cryptée style ssl. Dans les deux cas il faudrait aussi un token pour s'assurer que le post provient bien du formulaire. Le code donné dans mon premier message comprend à la fois un token et un hash du mot de passe avec grain de sel.

Eléphant du PHP | 290 Messages

06 nov. 2014, 15:30

Bonjour,
$req="SELECT nomutilisateur,motdepasse FROM identification WHERE nomutilisateur='".mysqli_real_escape_string($nombase,htmlentities($nomutilisateur_poste))."'
AND motdepasse='".md5(mysqli_real_escape_string($bdd,htmlentities($motdepasse_poste)))."'";
pour remplacer:
$req="SELECT nomutilisateur,motdepasse FROM identification";
qui lui marche, ne fonctionne pas.
Le but du remplacement étant de pouvoir se passer de extract

Je voudrais m'occuper du problème avec extract.
Je rappelle ce que je cherche à faire:

je veux comparer nom d'utilisateur posté + mot de passe posté avec
nom d'utilisateur dans bdd + mot de passe dans bdd.
Si pareil => l'utilisateur rentre dans sa session.
Sinon => il ne peut pas avoir accès à la session.
C'est du classique de chez classique.

Mon programme j'arrive à le faire mais je ne peux pas me passer de extract,
ce qui pose un problème car je ressors de la bdd des variables en plus
du nom d'utilisateur et du mot de passe concernés.
Cela représente une faille de sécurité car les variables ressorties inutilement
peuvent être profitable à un pirate d'une manière ou d'une autre.

J'ai conçu la récupération du nom d'utilisateur et du mot de passe en bdd concernés de la manière suivante:
Je ressors toutes mes données de ma table d'identification
(nom d'utilisteur et mot de passe concerné ainsi que les autres noms d'utilisateurs et les autres mots de passe dont je n'ai pas besoin)
et je compare ainsi avec les données postées:
if ($nomutilisateur_poste == $nomutilisateur && $motdepasse_poste == $motdepasse){
et je laisse le serveur comparer et prendre une décision en fonction du résultat.
Mais avec mon code initial, le serveur cherche d'abord parmi tous les $nomutilisateur
que j'ai sorti s'il y en a un dont la valeur est égale à la valeur du nom d'utilisateur postée,
et si parmi tous les $motdepasse que j'ai sorti il y en a un qui correspond à la valeur
du mot de passe posté.
Il y a donc un travail de recherche ligne par ligne.

Avec le code de xTG qui cible le nom de l'utilisateur et son mot de passe par rapport aux valeurs postées, et ce directement à travers le code SQL:
$req="SELECT nomutilisateur,motdepasse FROM identification WHERE login='".mysqli_real_escape_string($nombase,htmlentities($nomutilisateur_poste))."'
AND motdepasse='".md5(mysqli_real_escape_string($bdd,htmlentities($motdepasse_poste)))."'";
j'ai l'impression que le travail ligne par ligne dont je parle un peu plus haut n'est pas exécuté.
El pour analyser les problèmes si je remplace:
if ($nomutilisateur_poste == $nomutilisateur && $motdepasse_poste == $motdepasse){
par
if (isset($nomutilisateur) && isset($motdepasse)){
// je regarde si le nom d'utilisateur et le mot de passe correspondant dans ma bdd
//existent tous les deux et ont tous les deux une valeur non nulle (les variable ne sont pas vides)
j'observe à l'indentification sur le site que le test est négatif:
donc soient les variables n'existent pas, soient elles existent mais sont vides.
Si je remplace maintenant par:
if (empty($nomutilisateur) && empty($motdepasse)){
le test est positif,
c'est-à-dire que les deux variables existent mais sont vides.
Je recoupe les résultats du test de isset et de celui de empty et j'en conclu
que mes deux variables existent mais sont vides.

a. Le raisonnement tient-il la route déjà jusque là?

Si le code de xTG me fait effectivement ressortir les variables vides,
b. Pourquoi cela?
Je me demande si c'est pas parce-que le serveur ou peut-être plutôt le SGBD n'est pas programmé
pour faire automatiquement une recherche de correspondance
ligne par ligne à partir de code SQL, mais je crains aussi de faire là une conclusion farfelue.

Mais je crois en fait que je me retrouve avec un problème de conception au niveau
de ce petit bout de programmation.
En effet, pour récapituler mon utilisateur a posté des valeurs.
J'ai donc deux tâches à réaliser en vu de mes comparaisons:
- identifier dans ma table ma ligne concernée.
Avec les deux valeurs concernées du nom d'utilisateur et du mot de passe je pourrai comparer.
- comparer avec les valeurs postées.
xTG me dit donc, plutôt que tout récupérer, ce qui m'oblige à utiliser la fonction extract,
et ensuite regarder avec toutes les lignes de la table s'il y en a une dont les valeurs correspondent
aux valeurs postées, je me vois proposer d'identifier d'abord dans la table la lignes concernée
dont les valeurs doivent être comparées aux valeurs postées pour ensuite faire mes comparaisons
avec uniquement des valeurs réellement concernées par la comparaison.

Et cette identification se fait d'après xTG de la manière suivante:
$req="SELECT nomutilisateur,motdepasse FROM identification WHERE nomutilisateur='".mysqli_real_escape_string($nombase,htmlentities($nomutilisateur_poste))."'
AND motdepasse='".md5(mysqli_real_escape_string($bdd,htmlentities($motdepasse_poste)))."'";
ce qui sous-entend qu'on laisse le soin aux machines de comprendre qu'il faut faire une recherche dans toute la table
pour trouver parmi toutes les lignes celle où une valeur de $nomutilisateur correspond à $nomutilisateur_poste
et celle où une valeur de $motdepasse correspond à $motdepasse_poste.
c. Mais est-ce évident de comprendre ça via le code SQL ci-dessus pour les machines?
d. Si la réponse est oui, pourquoi à votre avis ça ne fonctionne pas?
Si la réponse est non, comment verriez-vous la conception de la recherche ligne par ligne
dans la table de la ligne appropriée pour que la comparaison entre des variables à comparer ensemble puisse être faite?

Je dois dire ne pas comprendre pourquoi cette petite fonction et ce petit bout de programmation me causent
autant de tracas #-o
Ce que je fais semble pourtant être très simple en programmation.
Quel mal de tête #-o #-o #-o

ViPHP
AB
ViPHP | 5818 Messages

06 nov. 2014, 23:30

J'ai lu en diagonale ... pas le courage de tout lire, il faudrait cibler tes questions.

Conseil : arrêtes de regarder ton code !
Sur le principe, il y a 50 milliards de façons de ne pas bien s'y prendre donc évidemment ça va te faire beaucoup de questions... :wink:
Faut faire des tutos, les comprendre, puis les adapter à tes besoins sinon tu t'en sortira pas. Je t'en ai donné un plus haut ici. En plus il est complet et tu peux donc le tester. Quand tu l'auras bien compris et tester, tu l'adapte à ton problème et éventuellement tu pose des questions. C'est en suivant des tutos qu'on apprend, pas en construisant du code sans avoir suffisamment de bases et se demander ensuite pourquoi cela ne marche pas. Regardes ce qui fonctionne, tu te poseras moins de questions !