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 :

Requêtes préparées et injections SQL


Sujet :

PHP & Base de données

  1. #1
    Membre émérite Avatar de Madfrix
    Profil pro
    Inscrit en
    Juin 2007
    Messages
    2 326
    Détails du profil
    Informations personnelles :
    Localisation : France, Gironde (Aquitaine)

    Informations forums :
    Inscription : Juin 2007
    Messages : 2 326
    Points : 2 566
    Points
    2 566
    Par défaut Requêtes préparées et injections SQL
    Bonjour,

    je sais que via les requêtes préparées, il est impossible de subir des attaques de type injection, mais je n'ai jamais compris le pourquoi du comment

    Est-ce PDO qui caste automatiquement les variables connaissant le type à l'avance des champs en base ou...?

    Merci de vos éclairssissements

  2. #2
    Membre chevronné Avatar de nosferapti
    Profil pro
    Inscrit en
    Avril 2009
    Messages
    1 157
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2009
    Messages : 1 157
    Points : 1 895
    Points
    1 895
    Par défaut
    PDO échappe automatiquement les données si tu les passe par les méthodes "bind*" ou par "execute"
    GNAP !

  3. #3
    Membre éclairé
    Profil pro
    Inscrit en
    Mars 2005
    Messages
    625
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2005
    Messages : 625
    Points : 822
    Points
    822
    Par défaut
    En prenant l'exemple que tu attaques une base mySql,

    PDO::PARAM_STRING appliquera un équivalent de mysql_real_escape_string() sur la variable liée.

    PDO::PARAM_INT castera la variable en entier, si tu lui passes une chaîne non numérique, ça te rentrera 0 en base.

    Edit : fichus smileys automatiques !
    Pourfendeur de singletons en croisade

  4. #4
    Membre expert Avatar de RunCodePhp
    Profil pro
    Inscrit en
    Janvier 2010
    Messages
    2 962
    Détails du profil
    Informations personnelles :
    Localisation : Réunion

    Informations forums :
    Inscription : Janvier 2010
    Messages : 2 962
    Points : 3 947
    Points
    3 947
    Par défaut
    Salut

    Citation Envoyé par Petibidon
    PDO::PARAM_INT castera la variable en entier, si tu lui passes une chaîne non numérique, ça te rentrera 0 en base.
    J'avais fais pas mal d'essai sur ce point là, et j'en avais conclu que PDO ne typait (ou castait) rien du tout.
    Les constantres PARAM_INT, PARAM_NULL, etc ... ne provoquait strictement rien sur les valeurs, c'est comme si on ne mettais rien.

    J'avais créé un topic la dessus pour avoir des avis d'ailleurs.
    -> BindValue() : Paramètre data_type sans effet ?
    Apparemment, et selon Julp ça dépendrait du SGBD, du driver/pilote utilisé.

    Pour PDO (Php5.3.0) et MySQL5.1.36, ça donne pas grand chose, du moins, selon mes essais.


    A coté de ça, il me semble que les requêtes préparées offrent énormément de sécurité contre les SQL injections.
    Win XP | WampServer 2.2d | Apache 2.2.21 | Php 5.3.10 | MySQL 5.5.20
    Si debugger, c'est supprimer des bugs, alors programmer ne peut être que les ajouter [Edsger Dijkstra]

  5. #5
    Expert éminent sénior

    Profil pro
    Inscrit en
    Juin 2002
    Messages
    6 152
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2002
    Messages : 6 152
    Points : 17 778
    Points
    17 778
    Par défaut
    C'est la séparation des données de la requête même (les valeurs des colonnes dans une requête update/insert ou au niveau de la clause where notamment) qui assure l'absence d'injections. C'est ensuite totalement géré par le SGBD. La requête préparée aura été analysée/compilée par le SGBD avant la prise en compte des valeurs des paramètres, ces dernières sont donc totalement indépendantes et sans influence.

    Les (potentielles) conversions effectuées par PHP ne sont pas liées, elles n'assurent la correspondance de type (PHP vers SGBD) que lorsque nécessaire (d'autant que certaines peuvent être implicitement réalisées par le SGBD).

  6. #6
    Membre expert Avatar de RunCodePhp
    Profil pro
    Inscrit en
    Janvier 2010
    Messages
    2 962
    Détails du profil
    Informations personnelles :
    Localisation : Réunion

    Informations forums :
    Inscription : Janvier 2010
    Messages : 2 962
    Points : 3 947
    Points
    3 947
    Par défaut
    Citation Envoyé par Julp
    Les (potentielles) conversions effectuées par PHP ne sont pas liées, elles n'assurent la correspondance de type (PHP vers SGBD) que lorsque nécessaire
    C'est vraiment potentielles, car pour MySQL ce n'est jamais le cas malheureusement.

    Si, (par exemple) on met comme paramètre PARAM_NULL et que coté Bdd le champ accepte une valeur nulle, et bien dans tous les cas ce n'est pas une valeur nulle qui sera insérée, au mieux, ça sera vide.
    Enfin, si la valeur n'est pas nulle j'entends. Ca veut bien dire que PARAM_NULL n'a aucune relation entre PDO et MySQL.
    Si on veux mettre une valeur nulle, on a pas d'autre choix que la valeur soit vraiment nulle, donc on doit traiter le cas avant PDO.
    C'est nulle ...


    Si ce paramètre n'est pas là pour résoudre ce genre cas (comme caster une variable), alors je dirais qu'il est trompeur, il laisserait sous entendre que ...
    Je serais tombé dans l'panneau, si on peu dire.


    Pour donner mon sentiment sur les requêtes préparées, et sauf erreur de ma part, vous me direz si je me trompe , mais il me semble que certains y feraient un peu trop confiance.

    Si on fait une requête comme celle ci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    $sql = 'SELECT champ FROM table WHERE user_id = '.$_POST['user_id'];
    $stmt = $pdo->prepare($sql);
    $stmt->execute();
    Ici, et théoriquement, on ne serait pas protégé contre une éventuelle injection venant de $_POST.

    Il faudrait au moins binder (si on peu dire) la valeur extérieure.
    Comme par exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    $sql = 'SELECT champ FROM table WHERE user_id = ?';
    $stmt = $pdo->prepare($sql);
    $stmt->execute(array($_POST['user_id']));
    Ou
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    $sql = 'SELECT champ FROM table WHERE user_id = :user_id';
    $stmt = $pdo->prepare($sql);
    $stmt->bindValue(':user_id', $_POST['user_id'], PDO::PARAM_INT);
    $stmt->execute();
    Ici on profite pleinement des requêtes préparées pour le coté sécurité.
    Win XP | WampServer 2.2d | Apache 2.2.21 | Php 5.3.10 | MySQL 5.5.20
    Si debugger, c'est supprimer des bugs, alors programmer ne peut être que les ajouter [Edsger Dijkstra]

  7. #7
    Expert éminent sénior

    Profil pro
    Inscrit en
    Juin 2002
    Messages
    6 152
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2002
    Messages : 6 152
    Points : 17 778
    Points
    17 778
    Par défaut
    Citation Envoyé par RunCodePhp Voir le message
    C'est vraiment potentielles, car pour MySQL ce n'est jamais le cas malheureusement.
    Ça n'a pas de lien direct avec les requêtes préparées de toute manière. Mais la cast n'est pas systématique car dépendant du SGBD au niveau de sa propre implémentation, API, conversions implicites qu'il peut réaliser, etc comme ce fut abordé dans la discussion précédemment pointée. (Je m'attendais cependant aussi à une cast plus systématique, ne serait-ce que par principe)

    Citation Envoyé par RunCodePhp Voir le message
    Si on fait une requête comme celle ci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    $sql = 'SELECT champ FROM table WHERE user_id = '.$_POST['user_id'];
    $stmt = $pdo->prepare($sql);
    $stmt->execute();
    Ici, et théoriquement, on ne serait pas protégé contre une éventuelle injection venant de $_POST.
    Un risque d'injection est bien présent ici. On exécute bien la requête comme une requête préparée mais elle n'a rien d'une requête préparée : la valeur de user_id devrait faire l'objet d'un paramètre, auquel on associe plus tard la valeur. Une telle utilisation des requêtes préparées n'a aucun intérêt et ce à tous les niveaux (performances comme sécurité). L'unique et bonne méthode est de passer par des paramètres puis de réaliser leur association à leurs valeurs respectives (méthodes bindParam, bindValue ou execute) tels les autres exemples.

    Pour illustrer : une requête préparée c'est une requête à trous (les paramètres : nommés - syntaxe :nom - ou anonymes - à base de points d'interrogation) dont le code SQL est statique. Ces "trous" sont ensuite remplacés (niveau SGBD) par des valeurs que l'ont fournies ensuite (méthodes bindParam, bindValue ou execute). Cela a plusieurs avantages : l'absence d'injections imputables à ces immondes concaténations ou autres interpolations et de pouvoir être exécutées plusieurs fois (que ces paramètres aient des valeurs différentes ou non - d'où le gain en performance d'ailleurs).

  8. #8
    Membre émérite Avatar de Madfrix
    Profil pro
    Inscrit en
    Juin 2007
    Messages
    2 326
    Détails du profil
    Informations personnelles :
    Localisation : France, Gironde (Aquitaine)

    Informations forums :
    Inscription : Juin 2007
    Messages : 2 326
    Points : 2 566
    Points
    2 566
    Par défaut
    Bonjour,

    tout d'abord merci pour vos réponses éclairées

    Citation Envoyé par RunCodePhp Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    $sql = 'SELECT champ FROM table WHERE user_id = '.$_POST['user_id'];
    $stmt = $pdo->prepare($sql);
    $stmt->execute();
    Ici, et théoriquement, on ne serait pas protégé contre une éventuelle injection venant de $_POST.
    On ne peut plus d'accord vu que je l'ai testé en local et que je me suis auto attaqué

    Mais comme l'a dit nosferapti :

    PDO échappe automatiquement les données si tu les passe par les méthodes "bind*" ou par "execute"
    Encore d'accord mais juste une question : ne serait ce pas plus simple (et encore plus sécurisé) que ce ne soit pas execute() qui échappe la string mais bien prepare() ? Ce serait finalement facilement implémentable non ?

    Ainsi que l'on execute via des ?, :id ou tout simplement une requete ne présentant pas d'intéret à etre préparée et bien dans totu ces cas de figure, l'echappement de prepare() nous assurerait (en théorie) une protection optimale non ?

  9. #9
    Expert éminent sénior

    Profil pro
    Inscrit en
    Juin 2002
    Messages
    6 152
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2002
    Messages : 6 152
    Points : 17 778
    Points
    17 778
    Par défaut
    Citation Envoyé par Madfrix Voir le message
    ne serait ce pas plus simple (et encore plus sécurisé) que ce ne soit pas execute() qui échappe la string mais bien prepare() ? Ce serait finalement facilement implémentable non ?
    Non, et c'est ce que j'explique depuis le début. La requête préparée n'est qu'une ossature, du SQL pur sans données lorsqu'elle est préparée via la méthode prepare. Elle n'a donc pas à être traitée/échappée, chose que le SGBD est tout aussi incapable de réaliser. Les données ne sont associées qu'après à leurs paramètres respectifs (méthodes bind[Param|Value] ou execute). Le traitement des données ne peut donc avoir lieu qu'à ce seul moment et non avant qu'elles ne soient connues. D'autant, je le répète, qu'une même requête préparée peut être exécutée plusieurs fois (en attribuant des données différentes ou non à ses paramètres).
    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
    $dbh = new PDO(DSN, LOGIN, MOT_DE_PASSE, $options);
     
    // Préparation de notre requête
    if (!$insert = $dbh->prepare('INSERT INTO `utilisateurs`(`login`, `mot_de_passe`, `email`) VALUES(?, SHA(?), ?)')) {
        list($pdoCode, $internalCode, $msg) = $dbh->errorInfo();
        die(sprintf("La préparation de la requête a échoué : %d/%d, %s", $pdoCode, $internalCode, $msg));
    }
     
    // Exécution, une première fois de la requête, avec nos valeurs
    if (!$insert->execute(array('Duvin.Marie', 'saphir', 'duvin.marie@bidule.fr'))) {
        list($pdoCode, $internalCode, $msg) = $insert->errorInfo();
        die(sprintf("L'exécution de la requête a échoué : %d/%d, %s", $pdoCode, $internalCode, $msg));
    }
    // Requête exécutée : INSERT INTO `utilisateurs`(`login`, `mot_de_passe`, `email`) VALUES('Duvin.Marie', SHA('saphir'), 'duvin.marie@bidule.fr');
     
    // Exécution, une deuxième fois de la requête, avec de nouvelles valeurs
    if (!$insert->execute(array('Morin.Hervé', 'zircon', 'morin.herve@bidule.fr'))) {
        list($pdoCode, $internalCode, $msg) = $insert->errorInfo();
        die(sprintf("L'exécution de la requête a échoué : %d/%d, %s", $pdoCode, $internalCode, $msg));
    }
    // Requête exécutée : INSERT INTO `utilisateurs`(`login`, `mot_de_passe`, `email`) VALUES('Morin.Hervé', SHA('zircon'), 'morin.herve@bidule.fr');
    Le réaliser lors du prepare serait revenir à la case départ puisqu'impossible : retour aux injections et/ou les requêtes préparées seraient sans intérêt.

Discussions similaires

  1. Requête analyse croisée sous SQL SERVER
    Par motus_z dans le forum MS SQL Server
    Réponses: 2
    Dernier message: 23/02/2006, 16h54
  2. Réponses: 10
    Dernier message: 25/10/2005, 16h09
  3. Requêtes analyses croisées sous SQL Server 2000
    Par callo dans le forum MS SQL Server
    Réponses: 3
    Dernier message: 24/09/2005, 19h27
  4. Pb Requête Corrélées sur MS SQL-SERVER2000
    Par Pongo dans le forum MS SQL Server
    Réponses: 2
    Dernier message: 21/09/2005, 16h08
  5. État, Requète et clause Where(SQL)
    Par Philippe299 dans le forum Access
    Réponses: 2
    Dernier message: 12/09/2005, 00h22

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