Perché preferiamo sempre utilizzare i parametri nelle istruzioni SQL?


114

Sono molto nuovo nel lavorare con i database. Ora posso scrivere SELECT, UPDATE, DELETE, e INSERTcomandi. Ma ho visto molti forum in cui preferiamo scrivere:

SELECT empSalary from employee where salary = @salary

...invece di:

SELECT empSalary from employee where salary = txtSalary.Text

Perché preferiamo sempre utilizzare i parametri e come li userei?

Volevo conoscere l'uso e i vantaggi del primo metodo. Ho persino sentito parlare di SQL injection ma non lo capisco completamente. Non so nemmeno se l'iniezione SQL è correlata alla mia domanda.


2
Hai ragione, questo è correlato all'iniezione SQL. Il modo in cui gestire i parametri è solitamente responsabilità di qualunque lingua / framework sia in esecuzione il programma e può dipendere dalla lingua. Pubblica sia il tuo RDBMS (utile) che il framwork ORM (necessario).
Clockwork-Muse

1
Sto usando C # come linguaggio di programmazione e Sql Server 2008 come database. Sto usando Microsoft dotNet framework 4.0. Mi dispiace davvero molto, non sono sicuro di quello che stai chiedendo (RDBMS o ORM), forse puoi darmi le mie versioni del framework RDBMS e ORM ora :-). Grazie mille
Sandy

1
RDBMS è il tuo database, nel tuo caso SQL Server 2008. Il tuo ORM è il metodo con cui accedi al tuo database, in questo caso ADO.NET. Altri includono LINQ to SQL ed Entity Framework . In effetti, una volta apprese le basi di ADO.NET e SQL, consiglio di utilizzare un ORM come LINQ o EF poiché si occupano di molti dei problemi che incontreresti scrivendo manualmente SQL.
Chad Levy,

Risposte:


129

L'utilizzo dei parametri aiuta a prevenire attacchi SQL Injection quando il database viene utilizzato insieme a un'interfaccia di programma come un programma desktop o un sito Web.

Nel tuo esempio, un utente può eseguire direttamente codice SQL sul tuo database creando istruzioni in txtSalary.

Ad esempio, se dovessero scrivere 0 OR 1=1, l'SQL eseguito sarebbe

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

per cui tutti i salari sarebbero stati restituiti.

Inoltre, un utente potrebbe eseguire comandi molto peggiori contro il tuo database, inclusa l'eliminazione se ha scritto 0; Drop Table employee:

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

La tabella employeeverrà quindi eliminata.


Nel tuo caso, sembra che tu stia usando .NET. Usare i parametri è facile come:

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

Modifica 2016-4-25:

Come da commento di George Stocker, ho cambiato il codice di esempio in modo che non lo usasse AddWithValue. Inoltre, si consiglia generalmente di racchiudere IDisposablei messaggi di posta elettronica nelle usingdichiarazioni.


ottima soluzione. Ma puoi spiegare un po 'di più, perché e come l'utilizzo dei parametri è sicuro. Voglio dire, sembra ancora che il comando sql sarà lo stesso
Sandy

possiamo aggiungere più parametri al comando sql. Come potremmo richiedere in un comando INSERT?
Sandy

2
SQL Server considera il testo all'interno dei parametri solo come input e non lo eseguirà mai.
Ciad Levy,

3
Sì, è possibile aggiungere più parametri: Insert Into table (Col1, Col2) Values (@Col1, @Col2). Nel tuo codice dovresti aggiungere più AddWithValues.
Ciad Levy,

1
Si prega di non utilizzare AddWithValue! Può causare problemi di conversione implicita. Impostare sempre la dimensione in modo esplicito e aggiungere il valore del parametro con parameter.Value = someValue.
George Stocker

75

Hai ragione, questo è correlato all'iniezione SQL , che è una vulnerabilità che consente a un utente malicioius di eseguire istruzioni arbitrarie sul tuo database. Questo fumetto XKCD preferito dai tempi antichi illustra il concetto:

Sua figlia si chiama Help I'm intrappolato in una fabbrica di patenti di guida.


Nel tuo esempio, se usi solo:

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

Sei aperto a SQL injection. Ad esempio, supponiamo che qualcuno inserisca txtSalary:

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

Quando esegui questa query, eseguirà un SELECTe un UPDATEo DROP, o qualunque cosa volessero. Alla --fine commenta semplicemente il resto della tua query, il che sarebbe utile nell'attacco se stessi concatenando qualcosa dopo txtSalary.Text.


Il modo corretto è utilizzare query con parametri, ad esempio (C #):

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

Con ciò, puoi eseguire in sicurezza la query.

Per riferimento su come evitare l'iniezione SQL in molte altre lingue, controllare bobby-tables.com , un sito Web gestito da un utente SO .


1
ottima soluzione. Ma puoi spiegare un po 'di più, perché e come l'utilizzo dei parametri è sicuro. Voglio dire, sembra ancora che il comando sql sarà lo stesso.
Sandy

1
@ user815600: un malinteso comune: credi ancora che la query con parametri prenda il valore e sostituisca i parametri con i valori effettivi, giusto? No, non sta succedendo! - invece, l'istruzione SQL con i parametri verrà trasmessa a SQL Server, insieme a un elenco di parametri e i loro valori - l'istruzione SQL non sarà la stessa
marc_s

1
ciò significa che sql injection viene monitorato dal meccanismo interno o dalla sicurezza di sql server. Grazie.
Sandy

4
Per quanto mi piacciano i cartoni animati, se esegui il codice con privilegi sufficienti per eliminare le tabelle, probabilmente hai problemi più ampi.
philw

9

Oltre ad altre risposte è necessario aggiungere che i parametri non solo aiutano a prevenire l'iniezione di sql, ma possono migliorare le prestazioni delle query . Il server SQL memorizza nella cache i piani di query parametrizzati e li riutilizza durante l'esecuzione di query ripetute. Se non hai parametrizzato la tua query, il server sql compilerebbe un nuovo piano su ogni esecuzione della query (con qualche esclusione) se il testo della query fosse diverso.

Ulteriori informazioni sulla memorizzazione nella cache del piano di query


1
Questo è più pertinente di quanto si possa pensare. Anche una query "piccola" può essere eseguita migliaia o milioni di volte, svuotando efficacemente l'intera cache delle query.
James,

5

Due anni dopo il mio primo tentativo , sto recidendo ...

Perché preferiamo i parametri? L'iniezione di SQL è ovviamente un motivo importante, ma potrebbe essere che desideriamo segretamente tornare a SQL come linguaggio . SQL in stringhe letterali è già una strana pratica culturale, ma almeno puoi copiare e incollare la tua richiesta nello studio di gestione. SQL costruito dinamicamente con condizionali del linguaggio host e strutture di controllo, quando SQL ha condizionali e strutture di controllo, è solo una barbarie di livello 0. Devi eseguire la tua app in debug, o con una traccia, per vedere quale SQL genera.

Non fermarti solo ai parametri. Vai fino in fondo e usa QueryFirst (disclaimer: che ho scritto). Il tuo SQL risiede in un file .sql. Puoi modificarlo nella favolosa finestra dell'editor TSQL, con convalida della sintassi e Intellisense per le tue tabelle e colonne. È possibile assegnare i dati del test nella sezione dei commenti speciali e fare clic su "Riproduci" per eseguire la query direttamente nella finestra. Creare un parametro è facile come inserire "@myParam" nel tuo SQL. Quindi, ogni volta che si salva, QueryFirst genera il wrapper C # per la query. I tuoi parametri compaiono, fortemente tipizzati, come argomenti dei metodi Execute (). I risultati vengono restituiti in un IEnumerable o in un elenco di POCO fortemente tipizzati, i tipi generati dallo schema effettivo restituito dalla query. Se la tua query non viene eseguita, la tua app non verrà compilata. Se lo schema del database cambia e la query viene eseguita ma alcune colonne scompaiono, l'errore di compilazione punta alla riga nel codiceche tenta di accedere ai dati mancanti. E ci sono numerosi altri vantaggi. Perché vorresti accedere ai dati in un altro modo?


4

In Sql quando una parola contiene il segno @ significa che è variabile e usiamo questa variabile per impostare il valore in essa e la usiamo sull'area dei numeri sullo stesso script sql perché è limitata solo al singolo script mentre puoi dichiarare molte variabili dello stesso tipo e nome su molti script. Usiamo questa variabile nel lotto della stored procedure perché le stored procedure sono query precompilate e possiamo passare valori in queste variabili da script, desktop e siti Web per ulteriori informazioni leggere Dichiarare la variabile locale , Sql Stored Procedure e sql injections .

Leggi anche Proteggi da sql injection che guiderà come proteggere il tuo database.

Spero che ti aiuti a capire anche qualsiasi domanda mi commenta.


3

Altre risposte spiegano perché i parametri sono importanti, ma c'è uno svantaggio! In .net, esistono diversi metodi per creare parametri (Add, AddWithValue), ma tutti richiedono di preoccuparsi, inutilmente, del nome del parametro e riducono la leggibilità dell'SQL nel codice. Proprio quando stai cercando di meditare sull'SQL, devi cercare sopra o sotto per vedere quale valore è stato utilizzato nel parametro.

Affermo umilmente che la mia piccola classe SqlBuilder è il modo più elegante per scrivere query con parametri . Il tuo codice sarà simile a questo ...

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

Il tuo codice sarà più breve e molto più leggibile. Non hai nemmeno bisogno di righe extra e, quando stai rileggendo, non hai bisogno di cercare il valore dei parametri. La classe di cui hai bisogno è qui ...

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

Assegnare un nome ai parametri aiuta sicuramente quando si profilano le query che il server sta eseguendo.
Dave R.

Il mio capo ha detto la stessa cosa. Se i nomi dei parametri significativi sono importanti per te, aggiungi un argomento paramName al metodo value. Sospetto che tu stia complicando inutilmente le cose.
bbsimonbb

Cattiva idea. Come è stato detto prima, AddWithValuepuò causare problemi di conversione implicita.
Adam Calvet Bohl

@Adam hai ragione, ma questo non impedisce a AddWithValue () di essere ampiamente utilizzato e non credo che invalidi l'idea. Ma nel frattempo, ho escogitato un modo molto migliore di scrivere query parametrizzate e questo non usa AddWithValue () :-)
bbsimonbb

Destra! Prometto che lo guarderò presto!
Adam Calvet Bohl

3

Vecchio post ma volevo garantire che i nuovi arrivati ​​fossero a conoscenza delle procedure memorizzate .

Il mio valore di 10 ¢ qui è che se sei in grado di scrivere la tua istruzione SQL come una procedura memorizzata , questo a mio avviso è l'approccio ottimale. Uso SEMPRE i proc memorizzati e non ho mai eseguito il loop dei record nel mio codice principale. Per esempio: SQL Table > SQL Stored Procedures > IIS/Dot.NET > Class.

Quando si utilizzano procedure memorizzate, è possibile limitare l'utente solo all'autorizzazione EXECUTE , riducendo così i rischi per la sicurezza .

La procedura memorizzata è intrinsecamente parametrizzata ed è possibile specificare parametri di input e output.

La procedura memorizzata (se restituisce dati tramite SELECTistruzione) può essere letta e letta esattamente allo stesso modo di un normale fileSELECT istruzione nel codice.

Inoltre viene eseguito più velocemente poiché viene compilato su SQL Server.

Ho anche detto che puoi eseguire più passaggi, ad esempio updateuna tabella, controllare i valori su un altro server DB e quindi, una volta terminato, restituire i dati al client, tutto sullo stesso server e nessuna interazione con il client. Quindi questo è MOLTO più veloce che codificare questa logica nel codice.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.