web-dev-qa-db-fra.com

Pourquoi préférons-nous toujours utiliser des paramètres dans les instructions SQL?

Je suis très nouveau pour travailler avec des bases de données. Maintenant, je peux écrire des commandes SELECT, UPDATE, DELETE et INSERT. Mais j'ai vu de nombreux forums où nous préférons écrire:

SELECT empSalary from employee where salary = @salary

...au lieu de:

SELECT empSalary from employee where salary = txtSalary.Text

Pourquoi préférons-nous toujours utiliser des paramètres et comment les utiliser?

Je voulais connaître l'utilisation et les avantages de la première méthode. J'ai même entendu parler de l'injection SQL mais je ne le comprends pas bien. Je ne sais même pas si l'injection SQL est liée à ma question.

102
Sandy

L'utilisation de paramètres permet d'éviter attaques par injection SQL lorsque la base de données est utilisée avec une interface de programme telle qu'un programme de bureau ou un site Web.

Dans votre exemple, un utilisateur peut exécuter directement du code SQL sur votre base de données en créant des instructions dans txtSalary.

Par exemple, s'ils devaient écrire 0 OR 1=1, le SQL exécuté serait

 SELECT empSalary from employee where salary = 0 or 1=1

par lequel tous les employés seraient retournés.

En outre, un utilisateur peut exécuter des commandes bien pires sur votre base de données, notamment en les supprimant s’il écrit 0; Drop Table employee:

SELECT empSalary from employee where salary = 0; Drop Table employee

La table employee serait alors supprimée.


Dans votre cas, il semble que vous utilisiez .NET. Utiliser les paramètres est aussi simple que:

C #

string sql = "SELECT empSalary from employee where salary = @salary";

using (SqlConnection connection = new SqlConnection(/* connection info */))
using (SqlCommand command = new SqlCommand(sql, connection))
{
    var salaryParam = new SqlParameter("salary", SqlDbType.Money);
    salaryParam.Value = txtMoney.Text;

    command.Parameters.Add(salaryParam);
    var results = command.ExecuteReader();
}

VB.NET

Dim sql As String = "SELECT empSalary from employee where salary = @salary"
Using connection As New SqlConnection("connectionString")
    Using command As New SqlCommand(sql, connection)
        Dim salaryParam = New SqlParameter("salary", SqlDbType.Money)
        salaryParam.Value = txtMoney.Text

        command.Parameters.Add(salaryParam)

        Dim results = command.ExecuteReader()
    End Using
End Using

Edit 2016-4-25:

Selon le commentaire de George Stocker, j'ai modifié le code de l'exemple afin de ne pas utiliser AddWithValue. En outre, il est généralement recommandé d’envelopper IDisposables dans les instructions using.

114
Chad Levy

Vous avez raison, cela est lié à injection SQL , une vulnérabilité qui permet à un utilisateur malicieux d’exécuter des instructions arbitraires sur votre base de données. Ce favori d'antan BD XKCD illustre le concept:

Her daughter is named Help I'm trapped in a driver's license factory.


Dans votre exemple, si vous utilisez simplement:

var query = "SELECT empSalary from employee where salary = " + txtSalary.Text;
// and proceed to execute this query

Vous êtes ouvert à l'injection SQL. Par exemple, disons que quelqu'un entre txtSalary:

1; UPDATE employee SET salary = 9999999 WHERE empID = 10; --
1; DROP TABLE employee; --
// etc.

Lorsque vous exécutez cette requête, il effectuera un SELECT et un UPDATE ou DROP, ou ce qu'ils voudraient. Le -- à la fin, commente simplement le reste de votre requête, ce qui serait utile lors d’une attaque si vous concaténéiez quoi que ce soit après txtSalary.Text.


La méthode correcte consiste à utiliser des requêtes paramétrées, par exemple (C #):

SqlCommand query =  new SqlCommand("SELECT empSalary FROM employee 
                                    WHERE salary = @sal;");
query.Parameters.AddWithValue("@sal", txtSalary.Text);

Avec cela, vous pouvez exécuter la requête en toute sécurité.

Pour savoir comment éviter l’injection SQL dans plusieurs autres langues, consultez bobby-tables.com , un site Web mis à jour par un tilisateur de SO .

68
NullUserException

En plus d’autres réponses, il faut ajouter que les paramètres aident non seulement à prévenir l’injection SQL, mais aussi à peuvent améliorer les performances des requêtes. Sql Server met en cache des plans de requête paramétrés et les réutilise lors de l'exécution répétée de requêtes. Si vous ne paramétrez pas votre requête, alors le serveur SQL compilerait un nouveau plan pour chaque requête (avec une exclusion), si le texte de la requête était différent.

Plus d'informations sur la mise en cache du plan de requête

5
Oleg

Deux ans après mon premier départ , je récidive ...

Pourquoi préférons-nous les paramètres? L’injection SQL est évidemment une raison importante, mais est-ce que nous aspirons secrètement à revenir à SQL en tant que langage. SQL dans les chaînes de caractères est déjà une pratique culturelle étrange, mais au moins, vous pouvez copier et coller votre demande dans le studio de gestion. SQL construit dynamiquement avec les structures de contrôle et de condition en langage hôte, lorsque SQL comporte des structures de contrôle et de condition, correspond à un niveau de barbarie de niveau 0. Vous devez exécuter votre application en mode débogage ou avec une trace pour voir le code SQL généré.

Ne vous arrêtez pas avec seulement des paramètres. Allez jusqu'au bout et utilisez QueryFirst (disclaimer: que j'ai écrit). Votre SQL vit dans un fichier .sql. Vous le modifiez dans la fabuleuse fenêtre de l'éditeur TSQL, avec validation de la syntaxe et Intellisense pour vos tables et colonnes. Vous pouvez affecter des données de test dans la section des commentaires spéciaux et cliquer sur "Jouer" pour exécuter votre requête directement dans la fenêtre. Créer un paramètre est aussi simple que de mettre "@myParam" dans votre code SQL. Ensuite, chaque fois que vous enregistrez, QueryFirst génère l'encapsuleur C # pour votre requête. Vos paramètres apparaissent, fortement typés, comme arguments des méthodes Execute (). Vos résultats sont renvoyés dans un IEnumerable ou une liste de POCO fortement typés, les types générés à partir du schéma réel renvoyé par votre requête. Si votre requête ne s'exécute pas, votre application ne sera pas compilée. Si votre schéma de base de données change et que votre requête s'exécute mais que certaines colonnes disparaissent, l'erreur de compilation pointe vers la ligne de votre code qui tente d'accéder aux données manquantes. Et il existe de nombreux autres avantages. Pourquoi voudriez-vous accéder aux données d'une autre manière?

3
bbsimonbb

Dans Sql, lorsque tout mot contient un signe @, cela signifie qu'il est variable et que nous l'utilisons pour définir une valeur et l'utiliser dans la zone numérique du même script SQL, car il est restreint uniquement sur un seul script, alors que vous pouvez déclarer beaucoup de variables de même type et nom sur de nombreux scripts. Nous utilisons cette variable dans le lot de procédures stockées car les procédures stockées sont des requêtes pré-compilées et nous pouvons transmettre des valeurs dans ces variables à partir de scripts, de postes de travail et de sites Web, pour obtenir des informations complémentaires. Declare Local Variable , Sql Procédure mémorisée et injections SQL .

Lisez également Protéger de l'injection SQL Cela vous indiquera comment protéger votre base de données.

J'espère que cela vous aidera à comprendre également toute question, commentez-moi.

3
Emaad Ali

D'autres réponses expliquent pourquoi les paramètres sont importants, mais il y a un inconvénient! Dans .net, il existe plusieurs méthodes pour créer des paramètres (Add, AddWithValue), mais elles vous obligent toutes à vous inquiéter inutilement du nom du paramètre et réduisent toutes la lisibilité du code SQL dans le code. Lorsque vous essayez de méditer sur le code SQL, vous devez rechercher au-dessus ou au-dessous pour voir quelle valeur a été utilisée dans le paramètre.

J'affirme humblement que ma petite classe SqlBuilder est le moyen le plus élégant d'écrire des requêtes paramétrées . Votre code va ressembler à ça ...

C #

var bldr = new SqlBuilder( myCommand );
bldr.Append("SELECT * FROM CUSTOMERS WHERE ID = ").Value(myId);
//or
bldr.Append("SELECT * FROM CUSTOMERS WHERE NAME LIKE ").FuzzyValue(myName);
myCommand.CommandText = bldr.ToString();

Votre code sera plus court et beaucoup plus lisible. Vous n'avez même pas besoin de lignes supplémentaires et, lorsque vous relisez, vous n'avez pas besoin de rechercher la valeur des paramètres. Le cours dont vous avez besoin est ici ...

using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.SqlClient;

public class SqlBuilder
{
private StringBuilder _rq;
private SqlCommand _cmd;
private int _seq;
public SqlBuilder(SqlCommand cmd)
{
    _rq = new StringBuilder();
    _cmd = cmd;
    _seq = 0;
}
public SqlBuilder Append(String str)
{
    _rq.Append(str);
    return this;
}
public SqlBuilder Value(Object value)
{
    string paramName = "@SqlBuilderParam" + _seq++;
    _rq.Append(paramName);
    _cmd.Parameters.AddWithValue(paramName, value);
    return this;
}
public SqlBuilder FuzzyValue(Object value)
{
    string paramName = "@SqlBuilderParam" + _seq++;
    _rq.Append("'%' + " + paramName + " + '%'");
    _cmd.Parameters.AddWithValue(paramName, value);
    return this;
}
public override string ToString()
{
    return _rq.ToString();
}
}
2
bbsimonbb

Ancienne publication, mais voulant que les nouveaux arrivants soient informés de procédures enregistrées.

Si je peux écrire votre instruction SQL en tant que procédure stockée , cela vaut à mon avis la meilleure approche. Je TOUJOURS utilise les procs stockés et ne parcourt jamais les enregistrements de mon code principal. Par exemple: SQL Table > SQL Stored Procedures > IIS/Dot.NET > Class.

Lorsque vous utilisez des procédures stockées, vous pouvez limiter l'utilisateur à EXÉCUTER UNIQUEMENT autorisation, donc réduction des risques pour la sécurité.

Votre procédure stockée est paramétrée de manière inhérente et vous pouvez spécifier des paramètres d'entrée et de sortie.

La procédure stockée (si elle renvoie des données via l'instruction SELECT) peut être consultée et lue exactement de la même manière qu'une instruction normale SELECT dans votre code.

Il s'exécute également plus rapidement car il est compilé sur SQL Server.

Ai-je également mentionné que vous pouvez effectuer plusieurs étapes, par exemple update une table, vérifiez les valeurs sur un autre serveur de base de données, puis, une fois l'opération terminée, renvoyez les données au client, le tout sur le même serveur et n'interagissez pas avec le client. C'est donc BEAUCOUP plus rapide que de coder cette logique dans votre code.

0
Arvin Amir