211 votes

Comment des instructions préparées peuvent-elles protéger contre les attaques par injection SQL?

Comment faire des déclarations préparées à l'avance de nous aider à prévenir l'injection SQL attaques?

Wikipédia dit:

Les requêtes préparées sont résistants contre les injections SQL, parce que les valeurs de paramètres, qui sont transmis à l'aide d'un autre plus tard protocole, n'a pas besoin d'être correctement échappé. Si l'original de la déclaration le modèle n'est pas dérivée de l'entrée externe, l'injection SQL ne peut pas se produire.

Je ne vois pas la raison très bien. Quelqu'un peut-il bien vouloir donner une explication simple (facile avec l'anglais que l'anglais n'est pas ma langue maternelle) et quelques exemples?

352voto

L'idée est très simple - la requête et les données sont envoyées au serveur SQL server séparément.
C'est tout.

La racine du problème de l'injection SQL est un mélange de la code et les données.

En fait, notre requête SQL est un complet programme légitime. Et nous sommes la création de ce programme de manière dynamique par l'ajout de données à la volée. Ainsi, ces données peuvent interférer avec le programme code et même de le modifier, comme chaque injection exemple le montre:

$expected_data = 1;
$query         = "SELECT * FROM users where id=$expected_data";

va produire régulièrement une requête

SELECT * FROM users where id=1

alors que ce code

$spoiled_data = "1; DROP TABLE users;"
$query        = "SELECT * FROM users where id=$spoiled_data";

produira malveillants séquence

SELECT * FROM users where id=1; DROP TABLE users;

Cela fonctionne parce que nous sommes en ajoutant directement les données dans le programme de corps et de devenir une partie du programme.

Ainsi, les données peuvent modifier le programme et en fonction des données passées, nous ont un flux régulier de la production ou de la table, users supprimé.

Alors que dans le cas de déclarations préparées à l'avance, nous ne modifions pas notre programme, il reste intact
C'est le point.

Nous envoyons le programme pour le premier serveur

$db->prepare("SELECT * FROM users where id=?");

où les données sont remplacés par des variable appelée "espace réservé".

Notez que la même requête envoyée au serveur, sans aucune des données qu'il contient! Et puis nous allons envoyer les données à la deuxième demande, totalement séparé de la requête elle-même:

$db->execute($data);

donc, il ne peut pas modifier notre programme et de faire du mal.
Assez simple, n'est-ce pas?

Cependant, il vaut la peine de noter que pas chaque fois que vous utilisez l'espace réservé, elle est traitée comme une déclaration préparée.

Espace réservé est une idée générale pour la substitution de données réelles à une variable pour l'avenir de la transformation, en instruction préparée est le seul sous-ensemble d'entre eux.

Parfois, nous avons à composer une requête avec les données, mais si tous les bits de données est correctement mis en forme en fonction de son type - rien de mal pourrait se produire.

Ainsi, par exemple, par défaut AOP ne pas utiliser les requêtes préparées , mais plutôt de composer la requête courante. Mais parce qu'il substitue à chaque espace réservé avec les données correctement formatées, rien de mal ne peut arriver.

Mais vous voulez dire à PDO pour faire la voie droite, à utiliser de vraies déclarations préparées à l'avance, vous devez définir ce paramètre:

$dbh->setAttribute( PDO::ATTR_EMULATE_PREPARES, false );

Veuillez également noter que le formatage n'est pas à confondre avec l'échappement. Mise en forme implique beaucoup plus d'actions que d'idiot de s'échapper. Donc, n'essayez pas de manuel d'échapper à la maison, au moins jusqu'à ce que vous apprenez à connaître chaque règle de mise en forme pour chaque requête distinct de la partie.

La seule chose que j'ai à ajouter, toujours omis dans chaque manuel:

Les instructions préparées peuvent protéger seulement les données, mais ne peut pas défendre le programme lui-même.
Donc, une fois que nous avons à ajouter, disons, une dynamique identifiant - un nom de champ, par exemple, les requêtes préparées ne peut pas nous aider. J'ai expliqué la question récemment, donc je ne vais pas me répéter.

25voto

Glenn Points 4638

Voici sql pour la configuration d'un exemple:

CREATE TABLE employee(name varchar, paymentType varchar, amount bigint);

INSERT INTO employee VALUES('aaron', 'salary', 100);
INSERT INTO employee VALUES('aaron', 'bonus', 50);
INSERT INTO employee VALUES('bob', 'salary', 50);
INSERT INTO employee VALUES('bob', 'bonus', 0);

L'Injecter classe est vulnérable à une injection sql. La requête est dynamiquement collés ensemble avec la saisie de l'utilisateur. L'intention de la requête pour afficher les informations au sujet de bob. Un salaire ou une prime basée sur la saisie de l'utilisateur. Mais le malicieux utilisateur manipule l'entrée de corrompre la requête en la clouant sur l'équivalent d'un " ou "vrai" à la clause where de sorte que tout est retourné, y compris les informations sur aaron, qui était censé être caché.

import java.sql.*;

public class Inject {

    public static void main(String[] args) throws SQLException {

        String url = "jdbc:postgresql://localhost/postgres?user=user&password=pwd";
        Connection conn = DriverManager.getConnection(url);

        Statement stmt = conn.createStatement();
        String sql = "SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType='" + args[0] + "'";
        System.out.println(sql);
        ResultSet rs = stmt.executeQuery(sql);

        while (rs.next()) {
            System.out.println(rs.getString("paymentType") + " " + rs.getLong("amount"));
        }

    }

}

L'exécution de cette, le premier cas est avec une utilisation normale, la seconde avec le malveillant d'injection:

c:\temp>java Inject salary
SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType='salary'
salary 50

c:\temp>java Inject "salary' OR 'a'!='b"
SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType='salary' OR 'a'!='b'
salary 100
bonus 50
salary 50
bonus 0

Vous ne devriez pas construire vos instructions sql avec la concaténation de chaîne de saisie de l'utilisateur. Non seulement est-il vulnérable à l'injection, mais elle a des implications de la mise en cache sur le serveur (la modification de la déclaration, donc moins de chances d'obtenir une instruction sql d'accès au cache alors que le bind exemple est toujours en cours d'exécution de la même déclaration).

Voici un exemple de Liaison pour éviter ce type d'injection:

import java.sql.*;

public class Bind {

    public static void main(String[] args) throws SQLException {

        String url = "jdbc:postgresql://localhost/postgres?user=postgres&password=postgres";
        Connection conn = DriverManager.getConnection(url);

        String sql = "SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType=?";
        System.out.println(sql);

        PreparedStatement stmt = conn.prepareStatement(sql);
        stmt.setString(1, args[0]);

        ResultSet rs = stmt.executeQuery();

        while (rs.next()) {
            System.out.println(rs.getString("paymentType") + " " + rs.getLong("amount"));
        }

    }

}

L'exécution de ce avec la même entrée que l'exemple précédent le montre le code malveillant ne fonctionne pas car il n'y a pas de paymentType correspondant à celle de la chaîne:

c:\temp>java Bind salary
SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType=?
salary 50

c:\temp>java Bind "salary' OR 'a'!='b"
SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType=?

16voto

Jose Points 41

En fait, avec les instructions préparées les données en provenance d'un pirate potentiel est traités comme des données, et il n'y a aucun moyen, il peut être mélangé avec votre application SQL et/ou être interprétée comme SQL (ce qui peut se produire lorsque les données transmis est placé directement dans votre application SQL).

C'est parce que les déclarations préparées à l'avance "préparer" la requête SQL d'abord de trouver une efficace plan de requête, et d'envoyer les valeurs réelles qui, vraisemblablement, de venir à partir d'un formulaire plus tard - au moment de la requête est exécutée.

Plus d'info ici:

Déclarations préparées à l'avance et de l'Injection SQL

4voto

lloydom Points 342

Dans SQL SERVER à l'aide d'une instruction préparée est-ce vraiment de l'injection de la preuve parce que les paramètres d'entrée dont le formulaire de la requête. Cela signifie que la requête exécutée n'est pas une requête dynamique. Exemple d'une injection sql vulnérables déclaration.

string sqlquery = "select * from table where username='" + inputusername +"' and password='" + pass + "'";

maintenant, si la valeur en inoutusername variable est quelque chose comme un' or 1=1 -- cette interrogation devient maintenant.

select * from table where username='a' or 1=1 -- and password=asda

et le reste est commenté après -- de sorte qu'il n'est jamais exécutée et contourné. à l'aide d'une instruction préparée exemple comme ci-dessous

Sqlcommand command = new sqlcommand("select * from table where username = @userinput and password=@pass");
command.Parameters.Add(new SqlParameter("@userinput", 100));
command.Parameters.Add(new SqlParameter("@pass", 100));
command.prepare();

Donc, en effet vous ne pouvez pas envoyer n'importe quelle autre paramètre en évitant l'injection sql..

3voto

Feisty Mango Points 7113

La phrase clé est need not be correctly escaped . Cela signifie que vous n'avez pas à vous soucier des gens qui essaient de lancer des tirets, des apostrophes, des citations, etc.

Tout est géré pour vous.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X