Approche De La Securite

Vous êtes ici : >> PHP / Approche De La Securite
Temps d'étude : 1h. Niveau : Moyen.
Tags : Approche De La Securite
Fichier(s) utile(s) pour ce cours : /

Espace membre



La Sécurité en PHP et la sécurité des sites web en général est un sujet très vaste, cela pourrait faire l'objet d'une formation entière.

Pour le moment, nous allons donc faire uniquement une sensibilisation par l'approche d'une des failles les plus connues : L'injection SQL.

Nous allons créer une nouvelle base de données que nous nommerons securite (pour l'occasion).

Modélisation et création de la base de données, table et champs.

Base de données : securite
Table : membre

Champ Type Taille Spécificité
id_membre INT 3 Clé primaire (PK - Primary Key), AUTO_INCREMENT (AI)
pseudo VARCHAR 20 UNIQUE
mdp VARCHAR 20 -
nom VARCHAR 20 -
prenom VARCHAR 20 -
email VARCHAR 20 -


Pour créer cette base de données, vous pouvez également passer par le gestionnaire de base de données PhpMyAdmin :
Accès à PhpMyAdmin :
explication PHP

Création d'une nouvelle base de données :
explication PHP

Création d'une nouvelle table :
explication PHP

Création de la structure de la table (champs/colonnes) :
explication PHP

Structure de la table (relecture) :
explication PHP

Insertion d'enregistrement (remplissage de la table) :
explication PHP



Pour créer cette base de données, vous pouvez également passer par la console Mysql :

Accès à la console Mysql :
explication PHP

Requête dans la console Mysql :
explication PHP

Voici le code à insérer :

Base de données securite - Table membre
	CREATE DATABASE securite ;
	
	USE securite ;
	
	CREATE TABLE membre (
	  id_membre int(3) NOT NULL AUTO_INCREMENT,
	  pseudo varchar(20) NOT NULL,
	  mdp varchar(20) NOT NULL,
	  nom varchar(20) NOT NULL,
	  prenom varchar(20) NOT NULL,
	  email varchar(50) NOT NULL,
	  PRIMARY KEY (id_membre),
	  UNIQUE KEY pseudo (pseudo)
	) ENGINE=InnoDB ;	
Nous allons insérer plusieurs enregistrement d'emblé :
	
	INSERT INTO utilisateur (id_membre, pseudo, mdp, nom, prenom, email) VALUES
	(1, 'Juju', 'soleil', 'Cottet', 'Julien', 'contact@monsite.com'),
	(2, 'LaMarie', 'planete', 'Thoyer', 'Marie', 'marie.thoyer@gmail.com'),
	(3, 'Laurence75', 'mars1980', 'Winter', 'Laurence', 'laurence75@hotmail.fr');
Le pseudo est déclaré comme étant unique afin que 2 internautes ne puissent pas prendre le même pseudo.

Création d'un dossier et d'un fichier connexion.php avec 1 ligne de code permettant la connexion à la base de données.

Une fois que la base de données a été créée, vous pouvez commencer à développer votre script.

/securite/
connexion.php

Dans cet exemple, nous ne ferons pas de page d'inscription afin de gagner du temps. Nous nous concentrerons directement sur la page de connexion :

connexion.php
	<?php
	$mysqli = new Mysqli('localhost', 'root', '', 'securite');	

Cette ligne de code nous permet de connecter notre page web (script) à la base de données.

Pour rappel, Msyqli est une classe permettant de se connecter à 1 base de données. Pour cela nous indiquons le nom du serveur, le pseudo, le mot de passe, le nom de la base de données.


Création d'un formulaire HTML (permettant la connexion des membres déjà inscrits)

Nous allons créer un formulaire (au format HTML) afin que les internautes puissent se connecter.

connexion.php
	<?php
	$mysqli = new Mysqli('localhost', 'root', '', 'securite');
	?>
	
	





Nous prévoyons 2 champs dans notre formulaire : pseudo & mdp.

Récupération et affichage des saisies en PHP (POST) sur la même page.

Maintenant que nous avons notre formulaire permettant la saisie d'identifiants de connexion, il nous faut prévoir du code de récupération afin de "capter" les saisies de l'internaute.

connexion.php
	<?php
	$mysqli = new Mysqli('localhost', 'root', '', 'securite');
	if($_POST)
	{
		echo "pseudo posté: $_POST[pseudo] 
"; echo "mdp posté: $_POST[mdp]
"; } ?>





Nous ajoutons une condition IF pour dire que si l'internaute nous poste quelque chose (cette action est liée au clic sur le bouton submit), et bien nous affichons les saisies de l'internaute pour être certain de les "capter", avant de tenter de connecter le membre.

L'affichage des saisies de l'internaute se fait grâce à la superglobale $_POST.


Requete SQL de selection (SELECT)

Maintenant que nous avons un système qui nous permet de recevoir des données sur la page web, il serait utile de faire une selection dans notre base afin de voir si ces informations sont reconnues (le membre existe t'il ?).

connexion.php
	<?php
	$mysqli = new Mysqli('localhost', 'root', '', 'securite');
	if($_POST)
	{
		//echo "pseudo posté: $_POST[pseudo] 
"; //echo "mdp posté: $_POST[mdp]
"; $req = "SELECT * FROM membre WHERE pseudo='$_POST[pseudo]' AND mdp='$_POST[mdp]'"; $résultat = $mysqli->query($req); echo 'requete debug : ' . $req . ''; $membre = $résultat->fetch_assoc(); if(!empty($membre)) { echo '

Vous êtes bien reconnu par le site web pour vous connecter...

'; echo "votre id est : " . $membre['id_membre'] . "
"; echo "votre pseudo est : " . $membre['pseudo'] . "
"; echo "votre mdp est : " . $membre['mdp'] . "
"; echo "votre nom est : " . $membre['nom'] . "
"; echo "votre prenom est : " . $membre['mdp'] . "
"; echo "votre email est : " . $membre['email'] . "
"; } else { echo '

Erreur d\'identification

'; } } ?>





Explications :

Une fois que l'internaute saisit un pseudo et un mot de passe, nous souhaitons vérifier dans notre base de données si les informations existent afin de pouvoir éventuellement le connecter et le faire accèder à son compte.

Pour cela, nous utilisons la méthode (fonction) query() de l'objet $mysqli afin de formuler une requête (de type SELECT, selection) qui va nous permettre de récupérer et d'afficher sur la page web les informations contenues dans notre base de données.

Nous récupérons les enregistrements dans la variable $résultat.
$résultat représente les enregistrements, techniquement c'est un objet Mysqli_result (c'est ce que la méthode query() renvoie lors d'une requête de selection).

La méthode fetch_assoc() utilisée sur l'objet Mysqli_result (par l'intermédiaire de la variable $résultat) permet de traiter les enregistrements (cela génère un tableau ARRAY) afin de pouvoir les exploiter.

Il y a qu'un seul internaute à récupérer à la fois, nous ne prévoyons donc pas de boucle while.

Nous prévoyons une condition IF supplémentaire pour demander si $membre n'est pas vide, c'est que l'on a rapatrié des informations sur un membre avec un pseudo et mdp existants.

Nous "piochons" à l'intérieur du tableau array $membre avec l'utilisation des crochets [].

Résultat
explication PHP


Attaque Injection SQL

Nous allons vous présenter une attaque (hacking) d'injection SQL pour détourner le comportement initialement prévu du site web, et bien entendu nous verrons comment s'en protéger.

Pour attaquer, tenter d'inscrire les informations suivantes (uniquement dans la case pseudo) :

pseudo : juju' #

pseudo : lamarie' #

pseudo : laurence75' #

Résultat
explication PHP

Nous inscrivons le pseudo de l'internaute suivi d'une quotes (symbole apostrophe) pour fermer la valeur à l'intérieur de la requête, nous plaçons ensuite un signe dieze pour que le reste de la requête soit mis en commentaire.

De cette manière la bonne apostrophe (quotes) sera ignorée, ainsi que le mot de passe. Cela nous permettra d'accèder à tous les comptes uniquement avec 1 pseudo.

Avec ce détournement, voici la requête SQL formulée par le serveur vers notre SGBD :

SELECT * FROM membre WHERE pseudo='Lamarie' #' AND mdp=''

La partie inscrite en rouge correspond à notre texte saisi

Le dieze # permet de mettre la suite de la requete en commentaire, on ne se préoccupe plus du mdp donc (°_°) !!!

Si nous ne connaissons pas le pseudo du membre, nous pouvons toujours tenter avec son id_membre :

Pour attaquer, tenter d'inscrire les informations suivantes (uniquement dans la case mdp) :

mdp : ' OR id_membre = '1

mdp : ' OR id_membre = '2

mdp : ' OR id_membre = '3

Résultat
explication PHP

Avec ce nouveau détournement, voici la requête SQL formulée par le serveur vers notre SGBD :

SELECT * FROM membre WHERE pseudo='' AND mdp='' OR id_membre = '1'

La partie inscrite en rouge correspond à notre texte saisi

Nous fermons l'apostrophe du champ mot de passe de la requête et nous en servons plus loin pour demander un id_membre en particulier.

Cela donne "selectionne moi le membre qui a un pseudo et un mot de passe vide OU celui qui a l'id_membre 1".

Personne ne possède de compte avec 1 pseudo et 1 mdp vide, mais il y a bien 1 membre qui a le numéro de membre 1 (°_°) !!!

Notons tout de même que nous avons orienté le code de manière à laisser la porte très ouverte à ces attaques afin de présenter les injections SQL. En régle général, les sites web se protègent contre ce type d'attaque.

En l'état, ces injections permettent de rentrer sur le compte d'un internaute ou avoir des informations sans être le propriétaire du compte.

Nous aurions pu aller plus loin et détruire une Base De Données, stopper le serveur, etc. mais nous allons nous arrêter là car il ne s'agit pas d'une introduction au piratage mais une sensibilisation aux failles de sécurité pour montrer de quelle manière cela peut exister.


Moyen de contre

Si vous souhaitez vous protéger de ce type d'attaque par injection SQL, vous pouvez faire appel à la fonction prédéfinie htmlspecialchar() ou encore htmlentities().

connexion.php
	<?php
	$mysqli = new Mysqli('localhost', 'root', '', 'securite');
	if($_POST)
	{
		$_POST['pseudo'] = htmlentities($_POST['pseudo'], ENT_QUOTES);
		$_POST['mdp'] = htmlentities($_POST['mdp'], ENT_QUOTES); 
		$req = "SELECT * FROM membre WHERE pseudo='$_POST[pseudo]' AND mdp='$_POST[mdp]'";
		$résultat = $mysqli->query($req);
		echo 'requete debug : ' . $req . '';
		$membre = $résultat->fetch_assoc();
		if(!empty($membre))
		{
			echo '

Vous êtes bien reconnu par le site web pour vous connecter...

'; echo "votre id est : " . $membre['id_membre'] . "
"; echo "votre pseudo est : " . $membre['pseudo'] . "
"; echo "votre mdp est : " . $membre['mdp'] . "
"; echo "votre nom est : " . $membre['nom'] . "
"; echo "votre prenom est : " . $membre['mdp'] . "
"; echo "votre email est : " . $membre['email'] . "
"; } else { echo '

Erreur d\'identification

'; } } ?>





Comme le dit la documentation officielle de PHP, la fonction prédéfinie htmlentities() convertit tous les caractères éligibles en entités HTML.

Pour le comprendre, il faut se rendre au niveau du code-source juste après l'attaque.

Résultat
explication PHP

Nous pouvons voir notamment que les chevrons ont été convertis.

Bien entendu, cela n'est qu'une introduction mais elle n'est pas suffisante pour protéger 1 site web complet, beaucoup d'autres types d'attaques existent.