In che modo un PreparedStatement evita o impedisce l'iniezione di SQL?


122

So che PreparedStatements evita / previene SQL Injection. Come lo fa? La query del modulo finale che viene costruita utilizzando PreparedStatements sarà una stringa o altrimenti?


3
Tecnicamente le specifiche JDBC non insistono sul fatto che non ci siano difetti di SQL injection. Non conosco nessuna unità interessata.
Tom Hawtin - tackline

3
@Jayesh suggerisco di aggiungere i contenuti del tuo blog come risposta qui. La maggior parte delle risposte indicano solo le differenze nella generazione di query SQL dinamiche b / n e nello stmt preparato. Non stanno affrontando il problema del perché le dichiarazioni preparate funzionano meglio come fa il tuo blog.
Pavan Manjunath

1
Aggiunto come risposta, spero che aiuti.
Jayesh

Risposte:


78

Il problema con SQL injection è che un input dell'utente viene utilizzato come parte dell'istruzione SQL. Utilizzando le istruzioni preparate è possibile forzare la gestione dell'input dell'utente come contenuto di un parametro (e non come parte del comando SQL).

Ma se non si utilizza l'input dell'utente come parametro per l'istruzione preparata, ma si crea invece il comando SQL unendo le stringhe, si è comunque vulnerabili alle iniezioni SQL anche quando si utilizzano istruzioni preparate.


1
Certo, ma puoi ancora codificare alcuni o tutti i tuoi parametri.
tangens

16
Esempio per favore - Ma se non usi l'input dell'utente come parametro per la tua istruzione preparata ma invece costruisci il tuo comando SQL unendo stringhe insieme, sei ancora vulnerabile alle iniezioni SQL anche quando usi istruzioni preparate.
david blaine

4
Le istruzioni preparate FWIW non sono una cosa JDBC - sono una cosa SQL. È possibile preparare ed eseguire istruzioni preparate da una console SQL. PreparedStatement li supporta solo dall'interno di JDBC.
beldaz

198

Considera due modi per fare la stessa cosa:

PreparedStatement stmt = conn.createStatement("INSERT INTO students VALUES('" + user + "')");
stmt.execute();

O

PreparedStatement stmt = conn.prepareStatement("INSERT INTO student VALUES(?)");
stmt.setString(1, user);
stmt.execute();

Se "utente" proveniva dall'input dell'utente e l'input dell'utente era

Robert'); DROP TABLE students; --

Quindi, in primo luogo, verresti lavato. Nel secondo, saresti al sicuro e Little Bobby Tables sarebbe registrato per la tua scuola.


8
Quindi, se ho capito bene, la query nel secondo esempio che verrà eseguito sarebbe in realtà: INSERT INTO student VALUES ("Robert '); DROP TABLE students; -") - o almeno qualcosa del genere. È vero?
Max

18
No, nel PRIMO caso, otterresti quella dichiarazione. Nella seconda, inserirà "Robert '); DROP TABLE students; -" nella tabella utente.
Paul Tomblin,

3
Questo è ciò che intendevo, nel secondo esempio (quello "sicuro"), la stringa Robert '); Studenti DROP TABLE; - verrà salvato nel campo nella tabella studenti. Ho scritto qualcos'altro? ;)
Max

7
Mi dispiace, cerco di evitare di annidare le virgolette a causa di confusione come questa. Ecco perché mi piacciono i PreparedStatements con i parametri.
Paul Tomblin,

59
Tavolini Bobby. XD Ottimo riferimento
Amalgovinus

129

Per capire come PreparedStatement impedisce SQL Injection, è necessario comprendere le fasi di esecuzione di SQL Query.

1. Fase di compilazione. 2. Fase di esecuzione.

Ogni volta che il motore di SQL Server riceve una query, deve passare attraverso le seguenti fasi,

Fasi di esecuzione delle query

  1. Fase di analisi e normalizzazione: in questa fase viene verificata la sintassi e la semantica della query. Controlla se la tabella di riferimenti e le colonne utilizzate nella query esistono o meno. Ha anche molte altre attività da svolgere, ma non entriamo nei dettagli.

  2. Fase di compilazione: in questa fase, le parole chiave utilizzate nella query come seleziona, da, dove ecc. Vengono convertite in un formato comprensibile dalla macchina. Questa è la fase in cui viene interpretata la query e viene decisa l'azione corrispondente da intraprendere. Ha anche molte altre attività da svolgere, ma non entriamo nei dettagli.

  3. Piano di ottimizzazione della query: in questa fase, viene creato l'albero decisionale per individuare i modi in cui è possibile eseguire la query. Individua il numero di modi in cui la query può essere eseguita e il costo associato a ciascuna modalità di esecuzione della query. Sceglie il piano migliore per eseguire una query.

  4. Cache: il miglior piano selezionato nel piano di ottimizzazione delle query viene archiviato nella cache, in modo che ogni volta che arriva la stessa query la volta successiva, non deve passare nuovamente attraverso la fase 1, la fase 2 e la fase 3. La prossima volta che la query arriverà, verrà controllata direttamente nella cache e prelevata da lì per l'esecuzione.

  5. Fase di esecuzione: in questa fase, la query fornita viene eseguita ei dati vengono restituiti all'utente come ResultSetoggetto.

Comportamento dell'API PreparedStatement nei passaggi precedenti

  1. Le PreparedStatement non sono query SQL complete e contengono segnaposto, che in fase di esecuzione vengono sostituiti dai dati forniti dall'utente.

  2. Ogni volta che qualsiasi PreparedStatment contenente segnaposto viene passato al motore di SQL Server, passa attraverso le fasi seguenti

    1. Fase di analisi e normalizzazione
    2. Fase di compilazione
    3. Query Optimization Plan
    4. Cache (le query compilate con segnaposto vengono archiviate nella cache).

UPDATE user set username =? e password =? WHERE id =?

  1. La query sopra verrà analizzata, compilata con segnaposto come trattamento speciale, ottimizzata e memorizzata nella cache. La query in questa fase è già compilata e convertita in un formato comprensibile dalla macchina. Quindi possiamo dire che la query archiviata nella cache è precompilata e solo i segnaposto devono essere sostituiti con i dati forniti dall'utente.

  2. Ora in fase di esecuzione, quando arrivano i dati forniti dall'utente, la query precompilata viene prelevata dalla cache e i segnaposto vengono sostituiti con i dati forniti dall'utente.

PrepareStatementWorking

(Ricorda, dopo che i segnaposto sono stati sostituiti con i dati dell'utente, la query finale non viene compilata / interpretata di nuovo e il motore di SQL Server tratta i dati dell'utente come dati puri e non un SQL che deve essere analizzato o compilato di nuovo; questa è la bellezza di PreparedStatement. )

Se la query non deve passare di nuovo attraverso la fase di compilazione, i dati sostituiti sui segnaposto vengono trattati come dati puri e non hanno alcun significato per il motore di SQL Server ed esegue direttamente la query.

Nota: è la fase di compilazione dopo la fase di analisi, che comprende / interpreta la struttura della query e fornisce un comportamento significativo ad essa. In caso di PreparedStatement, la query viene compilata solo una volta e la query compilata memorizzata nella cache viene raccolta continuamente per sostituire i dati dell'utente ed essere eseguita.

A causa della funzionalità di compilazione una tantum di PreparedStatement, è privo di attacchi SQL Injection.

Puoi ottenere una spiegazione dettagliata con un esempio qui: https://javabypatel.blogspot.com/2015/09/how-prepared-statement-in-java-prevents-sql-injection.html


3
bella spiegazione
Dheeraj Joshi

4
Letteralmente la risposta più completa sul pezzo COME funziona
jouell

È stato molto utile. Grazie per la spiegazione dettagliata.
sconosciuto

26

L'SQL utilizzato in PreparedStatement è precompilato nel driver. Da quel momento in poi, i parametri vengono inviati al driver come valori letterali e non parti eseguibili di SQL; quindi nessun SQL può essere iniettato utilizzando un parametro. Un altro effetto collaterale vantaggioso di PreparedStatements (precompilazione + invio solo di parametri) è il miglioramento delle prestazioni quando si esegue l'istruzione più volte anche con valori diversi per i parametri (supponendo che il driver supporti PreparedStatements) poiché il driver non deve eseguire l'analisi SQL e la compilazione ciascuno volta che i parametri cambiano.


Non deve essere implementato in questo modo, e credo che spesso non lo sia.
Tom Hawtin - tackline

4
In realtà l'SQL è tipicamente precompilato sul database. Cioè, un piano di esecuzione viene preparato sul database. Quando si esegue la query, il piano viene eseguito con quei parametri. Il vantaggio aggiuntivo è che la stessa istruzione può essere eseguita con parametri diversi senza che Query Processor debba compilare ogni volta un nuovo piano.
beldaz

3

I guess che sarà una stringa. Ma i parametri di input verranno inviati al database e le conversioni / cast appropriate verranno applicate prima di creare un'istruzione SQL effettiva.

Per darti un esempio, potrebbe provare a vedere se CAST / Conversion funziona.
Se funziona, potrebbe creare una dichiarazione finale.

   SELECT * From MyTable WHERE param = CAST('10; DROP TABLE Other' AS varchar(30))

Prova un esempio con un'istruzione SQL che accetta un parametro numerico.
Ora prova a passare una variabile stringa (con contenuto numerico accettabile come parametro numerico). Genera qualche errore?

Ora prova a passare una variabile stringa (con contenuto non accettabile come parametro numerico). Guarda cosa succede?


3

L'istruzione preparata è più sicura. Convertirà un parametro nel tipo specificato.

Ad esempio stmt.setString(1, user);convertirà il userparametro in una stringa.

Supponiamo che il parametro contenga una stringa SQL contenente un comando eseguibile : l'utilizzo di un'istruzione preparata non lo consentirà.

Aggiunge metacarattere (ovvero conversione automatica) a questo.

Questo lo rende più sicuro.


2

SQL injection: quando l'utente ha la possibilità di inserire qualcosa che potrebbe essere parte dell'istruzione sql

Per esempio:

Query stringa = "INSERT INTO students VALUES ('" + user + "')"

quando l'utente immette "Robert"); Studenti DROP TABLE; - "come input, provoca l'iniezione SQL

In che modo una dichiarazione preparata lo impedisce?

Query stringa = "INSERT INTO students VALUES ('" + ": name" + "')"

parameters.addValue ("nome", utente);

=> quando l'utente inserisce di nuovo "Robert"); Studenti DROP TABLE; - ", la stringa di input è precompilata sul driver come valori letterali e immagino che possa essere lanciata come:

CAST ( 'Robert'); Studenti DROP TABLE; - "AS varchar (30))

Quindi alla fine, la stringa verrà letteralmente inserita come nome della tabella.

http://blog.linguiming.com/index.php/2018/01/10/why-prepared-statement-avoids-sql-injection/


1
Se non sbaglio, la parte CAST(‘Robert’);da CAST(‘Robert’); DROP TABLE students; –‘ AS varchar(30))si spezzerebbe, quindi procederebbe con l'eliminazione della tabella se così fosse. Ferma l'iniezione, quindi credo che l'esempio non sia abbastanza completo per spiegare lo scenario.
Héctor Álvarez

1

Discorso preparato:

1) La precompilazione e la memorizzazione nella cache lato DB dell'istruzione SQL porta a un'esecuzione più rapida e alla capacità di riutilizzare la stessa istruzione SQL in batch.

2) Prevenzione automatica degli attacchi di SQL injection mediante l'escape incorporata di virgolette e altri caratteri speciali. Notare che ciò richiede l'utilizzo di uno qualsiasi dei metodi setXxx () PreparedStatement per impostare il valore.


1

Come spiegato in questo post , il PreparedStatementsolo non ti aiuta se stai ancora concatenando stringhe.

Ad esempio, un malintenzionato può ancora fare quanto segue:

  • chiamare una funzione di sospensione in modo che tutte le connessioni al database siano occupate, rendendo quindi l'applicazione non disponibile
  • estrarre dati sensibili dal DB
  • bypassando l'autenticazione dell'utente

Non solo SQL, ma anche JPQL o HQL possono essere compromessi se non si utilizzano parametri di bind.

In conclusione, non dovresti mai usare la concatenazione di stringhe durante la creazione di istruzioni SQL. Usa un'API dedicata a tale scopo:


1
Grazie per aver sottolineato l'importanza di utilizzare l'associazione di parametri, piuttosto che PreparedStatement da solo. La tua risposta, tuttavia, sembra implicare che l'utilizzo di un'API dedicata è necessario per proteggerti dall'iniezione SQL. Poiché questo non è il caso e funziona anche l'uso di PreparedStatement con l'associazione di parametri, ti andrebbe di riformulare?
Wild Pottok

-3

Nelle istruzioni preparate l'utente è obbligato a inserire i dati come parametri. Se l'utente inserisce alcune istruzioni vulnerabili come DROP TABLE o SELECT * FROM USERS, i dati non saranno interessati poiché verrebbero considerati come parametri dell'istruzione SQL


Stessa risposta della risposta selezionata con meno precisioni.
Julien Maret
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.