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

PHP & Base de données Discussion :

Protection contre faille XSS, Injection SQL, et faille CSRF


Sujet :

PHP & Base de données

  1. #1
    Membre éclairé
    Inscrit en
    Mai 2008
    Messages
    241
    Détails du profil
    Informations personnelles :
    Âge : 39

    Informations forums :
    Inscription : Mai 2008
    Messages : 241
    Par défaut Protection contre faille XSS, Injection SQL, et faille CSRF
    Bonjour à toutes et à tous,

    J'aimerai aborder ce point pour être certain d'avoir bien compris ces points et d'avoir mis en place les bonnes protections.

    A - Injection SQL et faille XSS

    En effet, en parcourant un site j'ai découvert cette manière de procéder :

    1 - Je récupère des informations depuis l'extérieur
    2 - Je me protège de cette manière la :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    $_GET['ext01'] $_POST['ext02']
     
    $recuperation_get = striplashes($_GET['ext01']); //SQL
    $recuperation_get = mysql_real_escape_string($recuperation_get); //SQL
    $recuperation_get =htmlspecialchars($recuperation_get); //XSS
     
    même manière de procéder pour POST
     
    $recuperation_post = striplashes($_POST['ext02']); //SQL
    $recuperation_post = mysql_real_escape_string($recuperation_post); //SQL
    $recuperation_post =htmlspecialchars($recuperation_post); //XSS
    Je me pose des questions sur la partie injection SQL.
    1 - Si l'utilisation utilise dans un mot de passe par exemple, automatiquement on vient dénaturer son mot de passe avec striplashes
    2 - mysql_real_escape_string renvoie une erreur (fonction inconnue), l'alternative proposée est mysqli_real_escape_string qui renvoie aussi une erreur car la liste des paramètres à échapper n'est pas définie...

    Ces élément sont-ils réellement nécessaires pour se protéger contre l'injection SQL ?

    Dans d'autres exemples que j'ai trouvé, striplashes et mysql_real_escape_string n'ont jamais été proposés contre la faille injection SQL.
    Mais on parlait plutôt de requêtes préparés, et que ces requêtes préparées permettaient de se prémunir contre l'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
     
    //Cas INSERT INTO
    $rep=$acces_bdd->prepare('INSERT INTO maTAble ($recuperation_get, $recuperation_post) VALUES(:$recuperation_get, :$recuperation_post);
    $rep->execute(array(	
    'Colonne01' => $recuperation_get,
    'Colonne02' => $recuperation_post
    ));
     
    //Cas UPDATE
    $rep=$acces_bdd->prepare('UPDATE maTAble SET $recuperation_get =:$recuperation_get, $recuperation_post =:$recuperation_post  WHERE id=:id');
     
    $rep->execute(array(	
    'Colonne01' => $recuperation_get,
    'Colonne02' => $recuperation_post,
    'id' => $Ma_Recherche['id']
    ));
    Pour la faille XSS, rien de trop méchant à comprendre si ce n'est l'utilisation de htmlspecialchars.

    Bref suite à ces différentes manière de procéder... me voici ici pour profiter de votre expérience car je suis un peu perdu pour le coup.

    B - Jeton CSRF

    J'ai bien compris que l'objectif du jeton est de vérifier que le formulaire qui a été complété vient bien de notre propre site.
    Les différents exemples rencontrés placent ce jeton directement dans un champs masqué du formulaire et, lors du traitement du formulaire vérifie que $_POST['jeton'] existe et que sa valeur
    concorde avec $_SESSION['jeton'].

    Y'a t-il un intérêt à généraliser ce jeton pour toutes les données entrantes ?

    Par exemple un lien de désinscription à une newsletter ?
    La personne clique sur le lien, est redirigée vers une page de traitement où on en profite pour récupérer des données par l'intermédiaire de $_GET (a minima l'email de la personne qui ne veut plus être inscrite).
    Un jeton a-t-il un intérêt ici ? (la vous vous dites surement... mince il a rien compris aux jetons le type )
    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
     
    Exemple :
    Je reçois une demande sur mon site
    www.monsite.pagetraitement?desinscription=O&email=monemail@youpi.com
     
    if(isset($_GET['desinscription']) AND $_GET['desinscription']=="O")
    {
         //Ouverture de la table et je supprime la ligne ou je trouve l'email
    }
     
    VERSUS
    $jeton = $_SESSION['jeton']
    if(isset($_GET['desinscription']) AND $_GET['desinscription']=="O" AND isset($jeton) AND ($jeton == $_SESSION['jeton']))
    {
         //Ouverture de la table et je supprime la ligne ou je trouve l'email
    }
    Mon idée sous-jacente (ou mon incompréhension sous-jacente () est que si le jeton permet de nous assurer qu'une personne ne puisse faire passer un site H pour notre site,
    une protection qui est fonctionnelle pour $_POST le sera aussi pour $_GET, non ?

    D'avance merci pour vos retours

  2. #2
    Membre émérite
    Homme Profil pro
    Autre
    Inscrit en
    Juillet 2021
    Messages
    427
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Autre

    Informations forums :
    Inscription : Juillet 2021
    Messages : 427
    Par défaut
    Bonjour,

    La fonction mysql_real_escape_string() n'existe plus depuis PHP 7, à oublier.
    striplashes() est une vieille alternative pour l'échappement des caractères sql, à ne pas utiliser pour ca.

    mysqli_real_escape_string() permet l'échapement des caractères sql si on utilise l'API Mysqli sans requêtes préparées, l'inconvénient de cette API est qu'elle est restreinte à Mysql.
    Sur un nouveau projet sans contrainte spécifique, il est recommandé d'utilisé l'API PDO : https://www.php.net/manual/en/mysqli...i.choosing.php

    La méthode PDO::quote() est équivalent à mysqli_real_escape_string(), elle permet l'échappement des caractères sql si on utilise l'API PDO sans requêtes préparées.
    Au lieu d'utiliser la méthode quote(), le plus simple avec PDO est d'utiliser les requêtes préparées : https://www.php.net/manual/fr/pdo.pr...statements.php


    Il ne faut pas appliquer htmlspecialchars()/htmlentities() ou autre fonction similaire sur les données POST ou GET dès la récupération puisque tu vas en effet dénaturer les données.
    Stocker les données en bdd avec des entités html est à éviter pour pleins de bonnes raisons : https://zestedesavoir.com/articles/2...et-faille-xss/

    Les premiers contrôles sur les données devraient être de vérifier le type (is_string(), is_array(), is_numeric(), ...) et la valeur (empty(), filter_var(), mb_strlen(), preg_match(), ...).
    Si les données valides sont à utiliser dans une requête sql, on peut alors faire une requête préparée sans autre traitement sur les données.
    Enfin, htmlspecialchars/htmlentities seront à utiliser uniquement à l'affichage des données dans un document html.
    https://www.php.net/manual/fr/securi...abase.avoiding

  3. #3
    Membre éclairé
    Inscrit en
    Mai 2008
    Messages
    241
    Détails du profil
    Informations personnelles :
    Âge : 39

    Informations forums :
    Inscription : Mai 2008
    Messages : 241
    Par défaut
    Bonjour Pytet,

    Je te remercie pour ton message détaillé.
    J'ai parcouru toute la doc que tu as listé.
    J'ai toujours eu un peu de mal avec la doc PHP pour être honnête.

    Pour être certain de bien avoir compris, je me permets de reformuler les choses à base d'un fil rouge (et de questions complémentaires).
    En cas d'erreur de compréhension, me dire l'étape où je me suis planté svp
    Je sais que mon message est un peu long mais la validation des étapes ci-dessous (ou leur invalidation) me permet de comprendre ce que je fais de bien ou de continuer à creuser ou je ne suis pas encore au clair.
    D'avance un grand merci pour l'aide qui sera apportée

    PROTECTION CONTRE INJECTION SQL/XSS :
    1 - Connexion conforme à la BDD de type :
    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
     
    <?php
     
    	// Connexion à la base de données
    	try
    	{
    		$acces_bdd = new PDO('mysql:host=localhost;dbname=nomdeladb;charset=utf8', 'utilisateur','mdp', 
    		array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION)  );
    	}
    	catch (Exception $e)
    	{
    		die('Erreur : ' . $e->getMessage());
    	}
     
    ?>
    Question :
    En parcourant la doc que tu as envoyé et certains liens dans cette même doc, il est conseillé que la connexion à la base de données ne devrait pas se faire avec le profil 'admin' ou 'superUtilisateur'.
    L'utilisateur communiqué par défaut par le service d'hébergement est par défaut un 'superUtilisateur' de la BDD.
    Comment faire pour créer un profil utilisateur lamba (et comment mettre des restrictions) au travers duquel on se connectera à la BDD ?

    2 - Nous recevons des données de la part de $_GET ou $_POST que nous devons inscrire dans une table de la BDD
    Cas typique d'un formulaire d'inscription par exemple :
    a - Utilisation des filtres pour obtenir le type de données que l'on souhaite
    Je me suis amusé à tester sur une page créée à cet effet :
    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
     
    <html>
     
    	<h1>TESTS</h1>
     
    	<h2>Formulaire de test</h2>
     
    	<form action="index.php" method="post">
          <label>Ma chaine de caractère :</label>
          <input type="text" name="user_name"/></br>
    	  <input type="submit" value="Envoyer"/>
    	</form>
     
    	<h2>Résultat :</h2>
     
    	<?php
    	if(!isset($_POST['user_name']))
    	{
    		echo "Acune donnée entrée";
    	}
    	else
    	{
    		if(is_numeric($_POST['user_name'])) // Cas de la popup => passer is_numeric en is_string
    		{
    			$message=($_POST['user_name']);
    			//$message=htmlspecialchars($message); //Cas de la popup => Seule cette ligne permettra de désactiver l'affichage de la popup
    			echo $message;
    		}
    		else
    		{
    			echo "On accepte pas cela"; // Si on passe en is_string, le else ne sert à rien vu que tout sera interprété comme une chaine de caractère...
    		}
    	}
     
    	?>
    </html>
    is_string =>peu importe ce que l'on place dans un champs de type input="text", il validera tout comme étant du texte : j'ai un peu de mal à voir son utilité du coup.
    is_numeric => il fait bien la différence entre du texte et des chiffres
    ...
    Bien compris pour la vérification du type en étape 1 a partir de la récupération de données
    Bien compris pour la vérification de valeur en étape 2.

    Et si j'ai bien tout compris... ce genre de texte (popup) tapé dans un input :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    <script>alert("YOUPI");</script>
    ne pourra jamais être traité avec le couple type/valeur à partir du moment où l'attend de l'utilisateur qu'il :
    . Donne un pseudo
    . Donne un avis ...

    Du coup, si on souhaite afficher le message de cet utilisateur (par exemple avant validation), la on utilise
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
         $message=($_POST['user_name']);
         $message=htmlspecialchars($message);
    Pour désactiver le fonctionnement du code depuis notre page web

    Si le client a placé :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    <script>alert("YOUPI");</script>
    Il lira sur son écran : <script>alert("YOUPI");</script> (code désactivé => faille XSS comblée).

    En revanche si le client décide d'enregistrer son commentaire (insertion ou mise à jour), le commentaire part en étape 3 vers la table

    3 - INSERTION - MAJ dans une table de la BDD => requêtes préparées :

    Nous attendons un texte et nous recevons ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    <script>alert("YOUPI");</script>
    Ou un code plus méchant visant à attaquer la BDD

    $message = "Code plus méchant provenant de $_POST ou $_GET";

    Les données contenues dans $message et potentiellement problématiques pour la sécurité de la BDD seront automatiquement rendues inoffensive (la majorité si j'ai bien lues) par le serveur
    qui va traiter la demande si on utilise des requêtes préparées.
    Si je suis les informations que tu m'as communiqué,
    Mon exemple pour INSERER

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    //Cas INSERT INTO
    $rep=$acces_bdd->prepare('INSERT INTO maTAble ($recuperation_get, $recuperation_post) VALUES(:$recuperation_get, :$recuperation_post);
    $rep->execute(array(	
    'Colonne01' => $recuperation_get,
    'Colonne02' => $recuperation_post
    ));
    N'est pas tout à fait correct et sécurisé c'est cela ?

    Il devrait avoir la forme suivante :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    //Cas INSERT INTO
    $rep=$acces_bdd->prepare('INSERT INTO maTAble ($recuperation_get, $recuperation_post) VALUES(:$recuperation_get, :$recuperation_post);
    $rep->bindPram(':$recuperation_get', $recuperation_get)
    $rep->bindPram(':$recuperation_post', $recuperation_post)
     
    $recuperation_get = $_GET[''];
    $recuperation_post = $_POST[''];
    $rep-> execute();
    Ou alors ces deux code d'insertion sont tous les deux sécurisés mais le dernier code (celui avec bindParam) permet juste de pouvoir insérer plusieurs lignes en changeant simplement la dernière partie :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    $recuperation_get = $_GET[''];
    $recuperation_post = $_POST[''];
    $rep-> execute();
    Auquel cas si on a qu'une seule ligne ou peut se servir de l'un ou de l'autre, si plusieurs lignes a créer, on passe sur le second avec bindParam.
    Est ce bien cela ?

    Pour le cas de l'UPDATE j'aurai ainsi la transformation suivante :
    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
    //Cas UPDATE
    $rep=$acces_bdd->prepare('UPDATE maTAble SET $recuperation_get =:$recuperation_get, $recuperation_post =:$recuperation_post  WHERE id=:id');
     
    $rep->execute(array(	
    'Colonne01' => $recuperation_get,
    'Colonne02' => $recuperation_post,
    'id' => $Ma_Recherche['id']
    ));
     
    // deviendrait alors
    $rep=$acces_bdd->prepare('UPDATE maTAble SET $recuperation_get =:$recuperation_get, $recuperation_post =:$recuperation_post  WHERE id=:id');
    $rep->bindPram(':$recuperation_get', $recuperation_get)
    $rep->bindPram(':$recuperation_post', $recuperation_post)
     
    $recuperation_get = $_GET[''];
    $recuperation_post = $_POST[''];
    $rep-> execute();
    ));
     
    // C'est bien cela ?
    4 - Si ma compréhension du point 3 est correcte, alors les requêtes préparées vont permettre à elles seules de stopper la majorité des attaques par injection SQL.
    Y-at-il des choses supplémentaires à réaliser ?
    Ou la phrase "la majorité des attaques" est juste placée dans la documentation car on veut juste dire qu'aucune protection n'est certaine a 100% et viendra un jour ou elle sera franchie... et ou il faudra rajouter une couche de protection

    Merci d'avance pour vos retours afin d'être certain d'avoir compris et d'appliquer les bonnes pratiques.

Discussions similaires

  1. [PHP 5.4] protection contre injection sql et xss
    Par sinifer dans le forum Langage
    Réponses: 8
    Dernier message: 07/05/2013, 18h31
  2. [MySQL] Sécurité contre failles XSS et injections SQL et optimisation du formulaire
    Par kenjiendo dans le forum PHP & Base de données
    Réponses: 2
    Dernier message: 09/08/2011, 11h35
  3. [MySQL] Eviter les failles XSS et les injections SQL
    Par johnson95 dans le forum PHP & Base de données
    Réponses: 2
    Dernier message: 09/09/2010, 15h30
  4. Protection contre les SQL Injections ?
    Par kedare dans le forum JDBC
    Réponses: 9
    Dernier message: 05/05/2010, 10h42
  5. [Sécurité] protections php pour XSS, injections SQL, etc
    Par nintendoplayer dans le forum Langage
    Réponses: 1
    Dernier message: 20/03/2008, 08h57

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