it-swarm.com.de

Warum bevorzugen wir immer die Verwendung von Parametern in SQL-Anweisungen?

Ich bin sehr neu in der Arbeit mit Datenbanken. Jetzt kann ich SELECT, UPDATE, DELETE und INSERT Befehle schreiben. Aber ich habe viele Foren gesehen, in denen wir lieber schreiben:

SELECT empSalary from employee where salary = @salary

...anstatt:

SELECT empSalary from employee where salary = txtSalary.Text

Warum bevorzugen wir immer Parameter und wie würde ich sie verwenden?

Ich wollte die Verwendung und die Vorteile der ersten Methode kennen. Ich habe sogar von SQL Injection gehört, aber ich verstehe es nicht ganz. Ich weiß nicht einmal, ob SQL Injection mit meiner Frage zusammenhängt.

102
Sandy

Die Verwendung von Parametern verhindert SQL Injection-Angriffe, wenn die Datenbank in Verbindung mit einer Programmoberfläche wie einem Desktop-Programm oder einer Website verwendet wird.

In Ihrem Beispiel kann ein Benutzer SQL-Code direkt in Ihrer Datenbank ausführen, indem er Anweisungen in txtSalary erstellt.

Wenn sie zum Beispiel 0 OR 1=1 Schreiben würden, wäre das ausgeführte SQL

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

wobei alle empSalaries zurückgegeben würden.

Außerdem kann ein Benutzer weitaus schlechtere Befehle für Ihre Datenbank ausführen, einschließlich des Löschens. Wenn er 0; Drop Table employee Geschrieben hat:

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

Die Tabelle employee würde dann gelöscht.


In Ihrem Fall scheinen Sie .NET zu verwenden. Die Verwendung von Parametern ist so einfach wie:

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

Bearbeiten Sie 2016-4-25:

Gemäß dem Kommentar von George Stocker habe ich den Beispielcode geändert, um AddWithValue nicht zu verwenden. Außerdem wird allgemein empfohlen, IDisposables in using -Anweisungen einzufügen.

114
Chad Levy

Sie haben Recht, dies steht im Zusammenhang mit SQL Injection , einer Sicherheitsanfälligkeit, die es einem böswilligen Benutzer ermöglicht, beliebige Anweisungen für Ihre Datenbank auszuführen. Dieser alte Zeitliebling XKCD-Comic illustriert das Konzept:

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


Wenn Sie in Ihrem Beispiel nur Folgendes verwenden:

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

Sie sind offen für SQL-Injection. Angenommen, jemand gibt txtSalary ein:

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

Wenn Sie diese Abfrage ausführen, wird ein SELECT und ein UPDATE oder DROP ausgeführt, oder was auch immer sie wollten. Das -- Am Ende kommentiert einfach den Rest Ihrer Abfrage aus, was für den Angriff nützlich wäre, wenn Sie nach txtSalary.Text Etwas verketten würden.


Der richtige Weg ist die Verwendung von parametrisierten Abfragen, zB (C #):

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

Damit können Sie die Abfrage sicher ausführen.

Informationen zum Vermeiden von SQL-Injection in mehreren anderen Sprachen finden Sie unter bobby-tables.com , einer Website, die von einem SO-Benutzer verwaltet wird.

68

Zusätzlich zu anderen Antworten muss hinzugefügt werden, dass Parameter nicht nur die SQL-Injection verhindern, sondern auch die Leistung von Abfragen verbessern kann . SQL Server zwischenspeichert parametrisierte Abfragepläne und verwendet sie bei wiederholter Ausführung von Abfragen wieder. Wenn Sie Ihre Abfrage nicht parametrisiert haben, kompiliert sql server bei jeder Ausführung der Abfrage (mit einigen Ausschlüssen) einen neuen Plan, wenn der Abfragetext davon abweicht.

Weitere Informationen zum Zwischenspeichern von Abfrageplänen

5
Oleg

Zwei Jahre nach mein erstes Mal rezidiviere ich ...

Warum bevorzugen wir Parameter? SQL-Injection ist natürlich ein wichtiger Grund, aber könnte es sein, dass wir uns insgeheim danach sehnen, zurück zu SQL zu kommen als Sprache. SQL in String-Literalen ist bereits eine seltsame kulturelle Praxis, aber Sie können Ihre Anforderung zumindest kopieren und in das Management Studio einfügen. SQL, das dynamisch mit Bedingungen und Kontrollstrukturen der Host-Sprache erstellt wird, ist nur Barbarei der Stufe 0, wenn SQL Bedingungen und Kontrollstrukturen aufweist. Sie müssen Ihre App im Debug oder mit einer Ablaufverfolgung ausführen, um zu sehen, welche SQL generiert wird.

Hören Sie nicht nur mit Parametern auf. Gehen Sie den ganzen Weg und verwenden Sie QueryFirst (Haftungsausschluss: was ich geschrieben habe). Ihr SQL lebt in einer .sql-Datei. Sie bearbeiten es im fabelhaften TSQL-Editorfenster mit Syntaxüberprüfung und Intellisense für Ihre Tabellen und Spalten. Sie können Testdaten im Bereich "Spezielle Kommentare" zuweisen und auf "Abspielen" klicken, um Ihre Abfrage direkt im Fenster auszuführen. Das Erstellen eines Parameters ist so einfach wie das Einfügen von "@myParam" in Ihre SQL. Bei jedem Speichern generiert QueryFirst dann den C # -wrapper für Ihre Abfrage. Ihre Parameter werden mit starker Typisierung als Argumente für die Execute () -Methoden angezeigt. Ihre Ergebnisse werden in einer IEnumerable oder einer Liste stark typisierter POCOs zurückgegeben, die aus dem von Ihrer Abfrage zurückgegebenen tatsächlichen Schema generiert wurden. Wenn Ihre Abfrage nicht ausgeführt wird, wird Ihre App nicht kompiliert. Wenn sich Ihr DB-Schema ändert und Ihre Abfrage ausgeführt wird, aber einige Spalten verschwinden, zeigt der Kompilierungsfehler auf die Zeile in Ihrem Code, die versucht, auf die fehlenden Daten zuzugreifen. Und es gibt zahlreiche weitere Vorteile. Warum möchten Sie auf andere Weise auf Daten zugreifen?

3
bbsimonbb

Wenn ein Word in SQL ein @ -Zeichen enthält, bedeutet dies, dass es eine Variable ist, und wir verwenden diese Variable, um den Wert darin festzulegen und für den Nummernbereich im selben SQL-Skript zu verwenden, da sie nur auf das einzelne Skript beschränkt ist, während Sie viele Variablen deklarieren können gleichen Typs und Namens in vielen Skripten. Wir verwenden diese Variable in gespeicherten Prozeduren, da gespeicherte Prozeduren vorkompilierte Abfragen sind und wir Werte in dieser Variablen von Skripten, Desktops und Websites für weitere Informationen übergeben können. Lesen Sie Lokale Variable deklarieren , Sql Gespeicherte Prozedur und SQL-Injektionen .

Lesen Sie auch Vor SQL-Injection schützen . Hier erfahren Sie, wie Sie Ihre Datenbank schützen können.

Hoffe es hilft dir auch jede Frage zu verstehen, kommentiere mich.

3
Emaad Ali

Andere Antworten decken ab, warum Parameter wichtig sind, aber es gibt einen Nachteil! In .net gibt es verschiedene Methoden zum Erstellen von Parametern (Add, AddWithValue), bei denen Sie sich jedoch unnötigerweise um den Parameternamen kümmern müssen und die die Lesbarkeit der SQL im Code verringern. Gerade wenn Sie versuchen, über SQL zu meditieren, müssen Sie nach oben oder unten suchen, um zu sehen, welcher Wert im Parameter verwendet wurde.

Ich behaupte demütig, dass meine kleine SqlBuilder-Klasse die eleganteste Art ist, parametrisierte Abfragen zu schreiben . Ihr Code wird so aussehen ...

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();

Ihr Code wird kürzer und besser lesbar. Sie brauchen nicht einmal zusätzliche Zeilen, und wenn Sie zurücklesen, müssen Sie nicht nach dem Wert von Parametern suchen. Die Klasse, die Sie brauchen, ist hier ...

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

Alter Beitrag wollte aber sicherstellen, dass Neulinge auf Stored Procedures aufmerksam werden.

Mein Wert hier ist, dass, wenn Sie Ihre SQL-Anweisung als gespeicherte Prozedur schreiben können, dies aus meiner Sicht der optimale Ansatz ist. Ich IMMER verwende gespeicherte Prozeduren und durchlaufe niemals Datensätze in meinem Hauptcode. Beispielsweise: SQL Table > SQL Stored Procedures > IIS/Dot.NET > Class.

Wenn Sie gespeicherte Prozeduren verwenden, können Sie den Benutzer auf die Berechtigung AUSFÜHREN beschränken, sodass Sicherheitsrisiken reduzieren.

Ihre gespeicherte Prozedur ist von Natur aus paramerisiert, und Sie können Eingabe- und Ausgabeparameter angeben.

Auf die gespeicherte Prozedur (wenn sie Daten über die Anweisung SELECT zurückgibt) kann genauso zugegriffen und gelesen werden wie auf eine reguläre Anweisung SELECT in Ihrem Code.

Es wird auch schneller ausgeführt, da es auf dem SQL Server kompiliert wird.

Habe ich auch erwähnt, dass Sie mehrere Schritte ausführen können, z. update eine Tabelle, überprüfen Sie die Werte auf einem anderen DB-Server und geben Sie dann, wenn Sie fertig sind, alle Daten auf demselben Server und ohne Interaktion mit dem Client an den Client zurück. Das ist also VIEL schneller als das Codieren dieser Logik in Ihrem Code.

0
Arvin Amir