Perché il meccanismo di prevenzione dell'iniezione SQL si è evoluto nella direzione dell'utilizzo di query con parametri?


59

Per come la vedo io, gli attacchi SQL injection possono essere prevenuti:

  1. Screening, filtraggio, codifica dell'input con attenzione (prima dell'inserimento in SQL)
  2. Utilizzo di istruzioni preparate / query con parametri

Suppongo che ci siano pro e contro per ciascuno, ma perché il n. 2 è decollato e considerato più o meno il modo di fatto per prevenire gli attacchi di iniezione? È solo più sicuro e meno soggetto a errori o c'erano altri fattori?

A quanto ho capito, se il n. 1 viene usato correttamente e vengono prese in considerazione tutte le avvertenze, può essere altrettanto efficace del n. 2.

Disinfezione, filtraggio e codifica

C'era un po 'di confusione tra il mio significato di sanificazione , filtraggio e codifica . Dirò che per i miei scopi, tutto quanto sopra può essere considerato per l'opzione 1. In questo caso capisco che la sanificazione e il filtro hanno il potenziale per modificare o scartare i dati di input, mentre la codifica conserva i dati così come sono , ma li codifica correttamente per evitare attacchi di iniezione. Credo che la fuga di dati possa essere considerata come un modo per codificarli.

Query con parametri vs Libreria di codifica

Ci sono risposte in cui i concetti di parameterized queriese encoding librariesche sono trattati in modo intercambiabile. Correggimi se sbaglio, ma ho l'impressione che siano diversi.

La mia comprensione è che encoding libraries, indipendentemente dal fatto che siano sempre in grado di modificare il "Programma" SQL, perché stanno apportando modifiche allo stesso SQL, prima che venga inviato a RDBMS.

Parameterized queries dall'altro lato, inviare il programma SQL a RDBMS, che quindi ottimizza la query, definisce il piano di esecuzione della query, seleziona gli indici da utilizzare, ecc., quindi collega i dati, come l'ultimo passaggio all'interno di RDBMS si.

Libreria di codifica

  data -> (encoding library)
                  |
                  v
SQL -> (SQL + encoded data) -> RDBMS (execution plan defined) -> execute statement

Query con parametri

                                               data
                                                 |
                                                 v
SQL -> RDBMS (query execution plan defined) -> data -> execute statement

Significato storico

Alcune risposte affermano che storicamente, le query con parametri (PQ) sono state create per motivi di prestazioni e prima che gli attacchi di iniezione mirati ai problemi di codifica diventassero popolari. Ad un certo punto è diventato evidente che i PQ erano anche abbastanza efficaci contro gli attacchi di iniezione. Per mantenere lo spirito della mia domanda, perché PQ è rimasto il metodo di scelta e perché ha prosperato sopra la maggior parte degli altri metodi quando si trattava di prevenire gli attacchi di iniezione SQL?


1
I commenti non sono per una discussione estesa; questa conversazione è stata spostata in chat .
maple_shaft

23
Le dichiarazioni preparate non sono il risultato dell'evoluzione degli attacchi di iniezione SQL. Erano lì fin dall'inizio. La tua domanda si basa su una premessa errata.
user207421

4
Se pensi di essere più intelligente dei cattivi allora vai per # 1
paparazzo

1
"perché PQ è rimasto il metodo di scelta" Perché è il più semplice e robusto. Inoltre i vantaggi prestazionali di cui sopra per PQ. Non c'è davvero un aspetto negativo.
Paul Draper,

1
Perché è la soluzione corretta al problema di come eseguire query, anche se non era per il problema dell'iniezione SQL in un contesto di sicurezza . I moduli che richiedono l'escape e utilizzano i dati in-band con i comandi sono sempre un bug di progettazione perché sono soggetti a errori, contro-intuitivi e si rompono male se usati in modo errato. Vedi anche: shell scripting.
R ..

Risposte:


147

Il problema è che il n. 1 richiede di analizzare e interpretare in modo efficace l'intera variante SQL a cui stai lavorando in modo da sapere se sta facendo qualcosa che non dovrebbe. E mantieni aggiornato quel codice mentre aggiorni il tuo database. Ovunque accetti input per le tue domande. E non rovinare tutto.

Quindi sì, questo genere di cose fermerebbe gli attacchi di SQL injection, ma è assurdamente più costoso da implementare.


60
@dennis - Bene, qual è un preventivo nella tua variante SQL? "? '?”? U + 2018? \ U2018? Ci sono trucchi per separare le espressioni? Le tue subquery possono fare aggiornamenti? Ci sono molte cose da considerare.
Telastyn,

7
@Dennis ogni motore DB ha il suo modo di fare cose come sfuggire ai caratteri nelle stringhe. Ci sono molti buchi da inserire, specialmente se un'applicazione deve funzionare con più motori DB o essere compatibile con le versioni future dello stesso motore che potrebbero modificare alcune sintassi di query minori che potrebbero essere sfruttabili.

12
Un altro vantaggio delle dichiarazioni preparate è il guadagno in termini di prestazioni che si ottiene quando è necessario rieseguire la stessa query, con valori diversi. Inoltre, le istruzioni preparate possono sapere se un valore è veramente inteso come nulluna stringa o un numero e agire di conseguenza. Questo è molto buono per la sicurezza. E anche se esegui la query una volta, il motore DB la avrà già ottimizzata per te. Meglio ancora se è memorizzato nella cache!
Ismael Miguel,

8
@Dennis Mr. Henry Null ti ringrazierà per averlo fatto nel modo giusto.
Mathieu Guindon,

14
@Dennis il nome è irrilevante. Il problema è con il cognome. Vedi Stack Overflow , Programmers.SE , Fox Sports , Wired , BBC e qualsiasi altra cosa puoi visualizzare in una rapida ricerca su Google ;-)
Mathieu Guindon

80

Perché l'opzione 1 non è una soluzione. Schermare e filtrare significa rifiutare o rimuovere input non validi. Ma qualsiasi input potrebbe essere valido. Ad esempio l'apostrofo è un personaggio valido nel nome "O'Malley". Deve solo essere codificato correttamente prima di essere utilizzato in SQL, che è ciò che fa le istruzioni preparate.


Dopo aver aggiunto la nota, sembra che ti stia sostanzialmente chiedendo perché utilizzare una funzione di libreria standard anziché scrivere da zero il tuo codice funzionalmente simile? Dovresti sempre preferire le soluzioni di libreria standard alla scrittura del tuo codice. È meno lavoro e più mantenibile. Questo è il caso di qualsiasi funzionalità, ma soprattutto per qualcosa che è sensibile alla sicurezza non ha assolutamente senso reinventare la ruota da soli.


2
Questo è tutto (e quella era la parte mancante in altre due risposte, quindi +1). Dato come viene formulata la domanda, non si tratta di disinfettare l'input dell'utente, ma cito la domanda: "filtrare l'input (prima dell'inserimento)". Se la domanda ora riguarda la sanificazione dell'input, allora perché dovresti farlo tu stesso invece di lasciare che la libreria lo faccia (mentre, anche, perdendo l'opportunità di avere piani di esecuzione memorizzati nella cache, a proposito)?
Arseni Mourzenko,

8
@Dennis: disinfettare o filtrare significa rimuovere le informazioni. Codificare significa trasformare la rappresentazione dei dati senza perdere informazioni.
Jacques B

9
@Dennis: filtrare significa accettare o rifiutare l'input dell'utente. Ad esempio, "Jeff" verrebbe filtrato come input del campo "Età dell'utente", poiché il valore non è ovviamente valido. Se, invece di filtrare l'input, inizi a trasformarlo, ad esempio, sostituendo il carattere di virgoletta singola, stai facendo esattamente la stessa cosa delle librerie di database in cui usano query parametrizzate; in questo caso, la tua domanda è semplicemente "Perché dovrei usare qualcosa che esiste ed è stato scritto da esperti del settore, quando posso reinventare la ruota in ogni progetto?"
Arseni Mourzenko,

3
@Dennis: O\'Malleysta usando la barra per sfuggire alla citazione per un corretto inserimento (almeno in alcuni database). In MS SQL o Access può essere evitato con un preventivo aggiuntivo O''Malley. Non molto portatile se devi farlo da solo.
AbraCadaver,

5
Non posso dirti quante volte il mio nome è stato completamente respinto da un sistema. A volte ho anche visto errori causati dall'iniezione SQL solo usando il mio nome. Diamine, una volta mi è stato chiesto di cambiare il mio nome utente perché in realtà stavo rompendo qualcosa sul backend.
Alexander O'Mara,

60

Se stai tentando di eseguire l'elaborazione delle stringhe, non stai davvero generando una query SQL. Stai generando una stringa che può produrre una query SQL. C'è un livello di riferimento indiretto che apre molto spazio a errori e bug. È davvero sorprendente, dato che nella maggior parte dei contesti siamo felici di interagire con qualcosa a livello di programmazione. Ad esempio, se abbiamo una struttura di elenco e vogliamo aggiungere un elemento, di solito non facciamo:

List<Integer> list = /* a list of 1, 2, 3 */
String strList = list.toString();   /* to get "[1, 2, 3]" */
strList = /* manipulate strList to become "[1, 2, 5, 3]" */
list = parseList(strList);

Se qualcuno suggerisce di farlo, risponderesti giustamente che è piuttosto ridicolo e che si dovrebbe semplicemente fare:

List<Integer> list = /* ... */;
list.add(5, position=2);

Ciò interagisce con la struttura dei dati a livello concettuale. Non introduce alcuna dipendenza da come quella struttura potrebbe essere stampata o analizzata. Quelle sono decisioni completamente ortogonali.

Il tuo primo approccio è come il primo esempio (solo un po 'peggio): stai assumendo che possa costruire programmaticamente la stringa che verrà analizzata correttamente come query che desideri. Dipende dal parser e da un sacco di logica di elaborazione delle stringhe.

Il secondo approccio all'utilizzo di query preparate è molto più simile al secondo esempio. Quando si utilizza una query preparata, si analizza essenzialmente una pseudo-query che è legale ma contiene alcuni segnaposto e quindi si utilizza un'API per sostituire correttamente alcuni valori. Non dovrai più coinvolgere il processo di analisi e non dovrai preoccuparti di alcuna elaborazione delle stringhe.

In generale, è molto più facile e molto meno soggetto a errori interagire con le cose al loro livello concettuale. Una query non è una stringa, una query è ciò che ottieni quando analizzi una stringa o ne costruisci una a livello di codice (o qualunque altro metodo ti permetta di crearne una).

C'è una buona analogia tra le macro in stile C che eseguono una semplice sostituzione del testo e le macro in stile Lisp che generano codice arbitrario. Con le macro in stile C, puoi sostituire il testo nel codice sorgente e ciò significa che hai la possibilità di introdurre errori sintattici o comportamenti fuorvianti. Con le macro Lisp, stai generando codice nella forma in cui il compilatore lo elabora (ovvero, stai restituendo le strutture dati effettive che il compilatore elabora, non il testo che il lettore deve elaborare prima che il compilatore possa raggiungerlo) . Con una macro Lisp, tuttavia, non è possibile generare qualcosa che potrebbe essere un errore di analisi. Ad esempio, non è possibile generare (let ((ab) a .

Anche con le macro Lisp, puoi comunque generare codice errato, perché non devi necessariamente essere consapevole della struttura che dovrebbe essere lì. Ad esempio, in Lisp, (let ((ab)) a) significa "stabilisce un nuovo legame lessicale della variabile a al valore della variabile b, e quindi restituisce il valore di a", e (let (ab) a) significa "stabilisce nuovi vincoli lessicali delle variabili aeb, inizializzandoli entrambi su zero e quindi restituendo il valore di a." Entrambi sono sintatticamente corretti, ma significano cose diverse. Per evitare questo problema, è possibile utilizzare funzioni più semanticamente consapevoli e fare qualcosa del tipo:

Variable a = new Variable("a");
Variable b = new Variable("b");
Let let = new Let();
let.getBindings().add(new LetBinding(a,b));
let.setBody(a);
return let;

Con qualcosa del genere, è impossibile restituire qualcosa che è sintatticamente non valido, ed è molto più difficile restituire qualcosa che accidentalmente non è quello che volevi.


Buona spiegazione
Mike Partridge,

2
Mi hai perso per "buona analogia" ma ho votato in base alla spiegazione precedente. :)
Wildcard

1
Ottimo esempio! - E potresti aggiungere: a seconda del tipo di dati a volte non è nemmeno possibile o fattibile creare una stringa analizzabile. - Cosa succede se uno dei miei parametri è un campo di testo libero contenente una bozza della storia (~ 10.000 caratteri)? o cosa succede se un parametro è un'immagine JPG ? - L'unico modo quindi è una query parametrizzata
Falco,

In realtà no - è una descrizione piuttosto negativa sul perché le dichiarazioni preparate si sono evolute come difesa per l'iniezione sql. In particolare, l'esempio di codice è in java, che non esisteva quando le query con parametri erano sviluppate probabilmente nel periodo in cui C / C ++ erano considerati allo stato dell'arte. I database SQL hanno iniziato a essere utilizzati nei primi anni del periodo 1970-1980. MODO prima delle lingue di livello superiore dove popolare. Cavolo, direi che molti di loro sono venuti per semplificare il lavoro con i database (qualcuno di PowerBuilder?)
TomTom

@TomTom in realtà, sono d'accordo con la maggior parte dei tuoi contenuti. Ho solo implicitamente toccato l'aspetto della sicurezza qui. Su SO, rispondo a molte domande SPARQL (il linguaggio di query RDF, con alcune somiglianze con SQL) e molte persone si imbattono in problemi perché concatenano stringhe anziché utilizzare query con parametri. Anche senza attacchi di iniezione, le query parametrizzate aiutano a evitare bug / arresti anomali e anche gli errori / arresti anomali possono essere problemi di sicurezza, anche se non sono attacchi di iniezione. Quindi direi sempre di più: le query con parametri sono buone, anche se l'iniezione SQL non è stata un problema, e sono buone ...
Joshua Taylor,

21

Aiuta che l'opzione n. 2 è generalmente considerata una procedura consigliata poiché il database può memorizzare nella cache la versione non parametrizzata della query. Le query con parametri precedono il problema dell'iniezione di SQL di diversi anni (credo), è proprio così che puoi uccidere due uccelli con una fava.


10
L'iniezione di SQL è stata un problema sin dalla prima invenzione di SQL. Non è diventato un problema in seguito.
Servito il

9
@Servy Teoricamente sì. Praticamente è diventato un vero problema solo quando i nostri meccanismi di input sono entrati online, presentando un'enorme superficie d'attacco che chiunque può martellare.
Jan Doggen,

8
Le piccole tabelle Bobby non sono d'accordo sul fatto che sia necessario Internet né una base di utenti di grandi dimensioni per sfruttare l'iniezione SQL. E, naturalmente, le reti precedono SQL, quindi non è come se dovessi aspettare le reti una volta che SQL è uscito. Sì, le vulnerabilità di sicurezza sono meno vulnerabili quando l'applicazione ha una piccola base di utenti, ma sono comunque vulnerabilità di sicurezza e le persone le sfruttano quando il database stesso ha dati preziosi (e molti database molto antichi avevano dati molto preziosi, poiché solo le persone con database di valore potrebbe permettersi la tecnologia) ..
Servy

5
@Servy a mia conoscenza, SQL dinamico era una funzionalità relativamente recente; l'uso iniziale di SQL era principalmente precompilato / preelaborato con parametri per i valori (sia in entrata che in uscita), quindi i parametri nelle query potrebbero precedere l'iniezione di SQL nel software (forse non nelle query ad hoc / CLI).
Mark Rotteveel,

6
Potrebbero precedere la consapevolezza dell'iniezione SQL.
user253751,

20

Disse semplicemente: non lo fecero. La tua dichiarazione:

Perché il meccanismo di prevenzione dell'iniezione SQL si è evoluto nella direzione dell'utilizzo delle query con parametri?

è fondamentalmente imperfetto. Le query con parametri esistono da più tempo di quanto SQL Injection sia almeno ampiamente noto. Sono stati generalmente sviluppati come un modo per evitare la concentrazione di stringhe nella solita funzionalità "form for search" delle applicazioni LOB (Line of Business). Molti - MOLTI - anni dopo, qualcuno ha riscontrato un problema di sicurezza con la manipolazione delle stringhe.

Ricordo di aver fatto SQL 25 anni fa (quando Internet NON era ampiamente utilizzato - era solo all'inizio) e ricordo di aver fatto SQL vs IBM DB5 IIRC versione 5 - e che aveva già parametrizzato le query.


Grazie. Perché era necessario evitare la concatenazione di stringhe? Mi sembra che sarebbe una caratteristica utile. Qualcuno ha avuto un problema con esso?
Dennis,

3
Due in realtà. Innanzitutto, non è sempre del tutto banale: perché occuparsi dell'allocazione della memoria ecc. Quando non è necessario. In secondo luogo, nell'antichità la memorizzazione nella cache del lato sql del database delle prestazioni non era esattamente eccezionale: la compilazione di SQL era costosa. Come effetto collaterale dell'utilizzo di una istruzione preparata di un sql (che è da dove provengono i parametri), i piani di espirazione potrebbero essere riutilizzati. SQL Server ha introdotto la parametrizzazione automatica (per riutilizzare i piani di query anche senza parametri - sono dedotti e impliciti) Penso che sia 2000 o 2007 - da qualche parte nel mezzo, IIRC.
TomTom,

2
Avere query con parametri non elimina la possibilità di eseguire la concatenazione di stringhe. È possibile eseguire la concatenazione di stringhe per generare una query con parametri. Solo perché una funzione è utile non significa che sia sempre una buona scelta per un determinato problema.
JimmyJames,

Sì, ma come ho detto - quando sono stati inventati, l'SQL dinamico ha avuto un successo piuttosto decente;) Ancora oggi la gente ti dice che i piani di query SQL dinamici in SQL Server non vengono riutilizzati (il che è sbagliato dal momento che - hm - as Ho detto qualche punto tra il 2000 e il 2007 - quindi abbastanza lungo). Ai vecchi tempi volevi davvero le istruzioni PREPARED se esegui sql più volte;)
TomTom

La pianificazione della cache per SQL dinamico è stata infatti aggiunta a SQL Server 7.0, nel 1998 - sqlmag.com/database-performance-tuning/…
Mike Dimmick

13

Oltre a tutte le altre buone risposte:

Il motivo per cui # 2 è migliore è perché separa i tuoi dati dal tuo codice. Nel numero 1 i tuoi dati fanno parte del tuo codice ed è da lì che provengono tutte le cose cattive. Con # 1 ottieni la tua query e devi eseguire ulteriori passaggi per assicurarti che la tua query comprenda i tuoi dati come dati mentre in # 2 ottieni il tuo codice e il suo codice e i tuoi dati sono dati.


3
Separare codice e dati significa anche che le tue difese contro l'iniezione di codice ostile sono scritte e testate dal fornitore del database. Pertanto, se qualcosa trasmesso come parametro insieme a una query innocua finisce per distruggere o sovvertire il database, la reputazione della società di database è in linea e la tua organizzazione potrebbe persino denunciarli e vincere. Significa anche che se quel codice contiene un bug sfruttabile, le probabilità sono piuttosto buone che è il sito di qualcun altro in cui tutto si scatena, piuttosto che il tuo. (Basta non ignorare le correzioni di sicurezza!)
nigel222,

11

Le query con parametri, oltre a fornire la difesa per iniezione SQL, spesso hanno un ulteriore vantaggio di essere compilate una sola volta, quindi eseguite più volte con parametri diversi.

Dal punto di vista database SQL select * from employees where last_name = 'Smith'e select * from employees where last_name = 'Fisher'sono nettamente differenti e richiedono pertanto separata analisi, la compilazione e l'ottimizzazione. Occuperanno anche slot separati nell'area di memoria dedicata alla memorizzazione delle istruzioni compilate. In un sistema pesantemente caricato con un numero elevato di query simili con calcolo di parametri diversi e sovraccarico di memoria può essere sostanziale.

Successivamente, l'utilizzo di query con parametri spesso offre importanti vantaggi in termini di prestazioni.


Penso che questa sia la teoria (basata su dichiarazioni preparate usate per query con parametri). In pratica, dubito che questo sia davvero così spesso, poiché la maggior parte delle implementazioni preparerà-eseguirà-bind-eseguirà in una chiamata, quindi usa un'istruzione preparata diversa per ogni query con parametri a meno che tu non faccia passi espliciti per preparare effettivamente le istruzioni (e una libreria -level prepareè spesso abbastanza diverso da un livello SQL reale prepare).
Jcaron,

Le seguenti query sono anche diverse dal parser SQL: SELECT * FROM employees WHERE last_name IN (?, ?)e SELECT * FROM employees WHERE last_name IN (?, ?, ?, ?, ?, ?).
Damian Yerrick,

Si Loro hanno. Ecco perché MS ha aggiunto la cache del piano di query nel 1998 a SQL Server 7. Come in: Le tue informazioni sono vecchie di una generazione.
TomTom,

1
@ TomTom: la memorizzazione nella cache del piano di query non è la stessa della parametrizzazione automatica, a cui sembra che si stia suggerendo. Come in, leggi prima di pubblicare.
Mustaccio

@mustaccio In realtà almeno MS ha presentato entrambi contemporaneamente.
TomTom,

5

Aspetta, ma perché?

L'opzione 1 significa che devi scrivere routine di disinfezione per ogni tipo di input mentre l'opzione 2 è meno soggetta a errori e meno codice per poter scrivere / testare / mantenere.

Quasi certamente "prendersi cura di tutti gli avvertimenti" può essere più complesso di quanto si pensi, e il tuo linguaggio (ad esempio Java PreparedStatement) ha più sotto il cofano di quanto pensi.

Le istruzioni preparate o le query parametrizzate sono precompilate nel server di database, quindi, quando vengono impostati i parametri, non viene eseguita alcuna concatenazione SQL poiché la query non è più una stringa SQL. Un vantaggio aggiuntivo è che RDBMS memorizza nella cache la query e le chiamate successive sono considerate lo stesso SQL anche quando i valori dei parametri variano, mentre con SQL concatenato ogni volta che la query viene eseguita con valori diversi la query è diversa e RDBMS deve analizzarla , creare nuovamente il piano di esecuzione, ecc.


1
JDBC non disinfetta l'anithing. Il protocollo ha una parte specifica per il parametro e il DB semplicemente non interpreta tali parametri, pertanto è possibile impostare il nome della tabella dal parametro.
talex,

1
Perché? se il parametro non viene analizzato o interpretato non c'è motivo di sfuggire a qualcosa.
talex,

11
Penso che tu abbia l'immagine sbagliata di come funziona una query con parametri. Non è solo un caso in cui i parametri vengono sostituiti in un secondo momento, non vengono mai sostituiti . Un DBMS trasforma qualsiasi query in un "piano", una serie di passaggi che eseguirà per ottenere il risultato; in una query con parametri, quel piano è come una funzione: ha un numero di variabili che devono essere fornite quando viene eseguito. Quando vengono fornite le variabili, la stringa SQL è stata completamente dimenticata e il piano viene appena eseguito con i valori forniti.
IMSoP,

2
@IMSoP Era un mio malinteso. Anche se penso che sia comune come puoi vedere nelle due risposte più votate a questa domanda in SO stackoverflow.com/questions/3271249/… . Ne ho letto e hai ragione. Ho modificato la risposta.
Tulains Córdova,

3
@TomTom È fantastico per le prestazioni , ma non fa nulla per la sicurezza . Quando un componente SQL dinamico compromesso viene compilato e memorizzato nella cache, il programma è già stato modificato . La creazione di un piano da SQL parametrico non dinamico e il passaggio di elementi di dati è ancora sostanzialmente diverso da un DBMS che sottragga la somiglianza tra due query presentate come stringhe SQL complete.
IMSoP,

1

Immaginiamo come sarebbe un approccio ideale per "disinfettare, filtrare e codificare".

Disinfettare e filtrare potrebbe avere senso nel contesto di una particolare applicazione, ma alla fine entrambi si riducono a dire "non è possibile inserire questi dati nel database". Per la tua applicazione, potrebbe essere una buona idea, ma non è qualcosa che puoi raccomandare come soluzione generale, poiché ci saranno applicazioni che dovranno essere in grado di memorizzare caratteri arbitrari nel database.

In questo modo lascia la codifica. Puoi iniziare con una funzione che codifica le stringhe aggiungendo caratteri di escape, in modo da poterli sostituire in te stesso. Poiché database diversi necessitano di caratteri di escape diversi (in alcuni database, entrambi \'e ''sono sequenze di escape valide per ', ma non in altri), questa funzione deve essere fornita dal fornitore del database.

Ma non tutte le variabili sono stringhe. A volte è necessario sostituire un numero intero o una data. Questi sono rappresentati in modo diverso dalle stringhe, quindi sono necessari diversi metodi di codifica (di nuovo, questi dovrebbero essere specifici per il fornitore del database) e è necessario sostituirli nella query in diversi modi.

Quindi forse le cose sarebbero più facili se il database gestisse la sostituzione anche per te: sa già quali tipi si aspetta la query e come codificare i dati in modo sicuro e come sostituirli in modo sicuro nella query, quindi non devi preoccuparti nel tuo codice.

A questo punto, abbiamo appena reinventato le query con parametri.

E una volta parametrizzate le query, si aprono nuove opportunità, come l'ottimizzazione delle prestazioni e il monitoraggio semplificato.

La codifica è difficile da fare bene e la codifica-fatto-giusto è indistinguibile dalla parametrizzazione.

Se ti piace davvero l'interpolazione di stringhe come un modo per costruire query, ci sono un paio di lingue (ti vengono in mente Scala ed ES2015) che hanno un'interpolazione di stringhe inseribile, quindi ci sono librerie che ti permettono di scrivere query con parametri che sembrano interpolazione di stringhe, ma sono al sicuro dall'iniezione SQL, quindi nella sintassi ES2015:

import {sql} from 'cool-sql-library'

let result = sql`select *
    from users
    where user_id = ${user_id}
      and password_hash = ${password_hash}`.execute()

console.log(result)

1
"La codifica è difficile da fare bene" - hahaha. Non è. Un giorno o due, è tutto documentato. Ho scritto un codificatore molti anni fa per un ORM (perché il server sql ha un limite ai parametri e quindi è problematico inserire 5000-10000 righe in una sola istruzione (15 anni fa). Non ricordo che si tratti di un grosso problema.
TomTom,

1
Forse SQL Server è sufficientemente regolare da non costituire un problema, ma ho riscontrato problemi in altri DB: casi angolari con codifiche di caratteri non corrispondenti, opzioni di configurazione oscure, data specifica della locale e problemi numerici. Tutti risolvibili, ma necessitano almeno di una comprensione superficiale delle stranezze del DB (ti sto guardando, MySQL e Oracle).
James_pic,

3
@TomTom La codifica è in realtà molto difficile da ottenere quando si tiene conto del tempo. Cosa fai quando il tuo fornitore di database decide di creare un nuovo stile di commento nella prossima versione o quando una parola chiave diventa una nuova parola chiave in un aggiornamento? Potresti teoricamente ottenere la codifica effettivamente giusta per una versione del tuo RDBMS e sbagliarti alla prossima revisione. Non iniziare nemmeno su ciò che accade quando passi i fornitori a uno che ha commenti condizionali utilizzando una sintassi non standard
Eric,

@Eric, è francamente orribile. (Uso Postgres; se ha delle verruche così bizzarre, devo ancora incontrarle.)
Wildcard

0

Nell'opzione 1, stai lavorando con un set di input di size = infinity che stai cercando di mappare su una dimensione di output molto grande. Nell'opzione 2, hai limitato il tuo input a qualunque cosa tu scelga. In altre parole:

  1. Screening e filtro accurati [ infinito ] per [ tutte le query SQL sicure ]
  2. Utilizzo di [ scenari preconsiderati limitati al proprio ambito ]

Secondo altre risposte, sembrano esserci anche alcuni vantaggi prestazionali nel limitare il tuo raggio d'azione dall'infinito e verso qualcosa di gestibile.


0

Un utile modello mentale di SQL (in particolare dialetti moderni) è che ogni istruzione o query SQL è un programma. In un programma eseguibile binario nativo, i tipi più pericolosi di vulnerabilità della sicurezza sono gli overflow in cui un utente malintenzionato può sovrascrivere o modificare il codice del programma con istruzioni diverse.

Una vulnerabilità dell'iniezione SQL è isomorfa a un overflow del buffer in un linguaggio come C. La storia ha dimostrato che gli overflow del buffer sono estremamente difficili da prevenire - anche il codice estremamente critico soggetto a revisione aperta ha spesso contenuto tali vulnerabilità.

Un aspetto importante dell'approccio moderno alla risoluzione delle vulnerabilità di overflow è l'uso di meccanismi hardware e di sistema operativo per contrassegnare particolari parti della memoria come non eseguibili e per contrassegnare altre parti della memoria come di sola lettura. (Vedi l'articolo di Wikipedia sulla protezione degli spazi eseguibili , ad esempio.) In questo modo, anche se un utente malintenzionato potrebbe modificare i dati, l'utente malintenzionato non può fare in modo che i loro dati iniettati vengano trattati come codice.

Quindi, se una vulnerabilità dell'iniezione SQL è equivalente a un overflow del buffer, qual è l'equivalente SQL di un bit NX o delle pagine di memoria di sola lettura? La risposta è: istruzioni preparate , che includono query con parametri più meccanismi simili per richieste senza query. L'istruzione preparata viene compilata con alcune parti contrassegnate come di sola lettura, quindi un utente malintenzionato non può modificare quelle parti del programma e altre parti contrassegnate come dati non eseguibili (i parametri dell'istruzione preparata), in cui l'attaccante potrebbe inserire dati ma che non sarà mai trattato come un codice di programma, eliminando così la maggior parte del potenziale di abuso.

Certamente, disinfettare l'input dell'utente è buono, ma per essere veramente sicuri devi essere paranoico (o, equivalentemente, pensare come un attaccante). Una superficie di controllo al di fuori del testo del programma è il modo per farlo e istruzioni preparate forniscono tale superficie di controllo per SQL. Quindi non dovrebbe sorprendere che dichiarazioni preparate, e quindi query parametrizzate, siano l'approccio raccomandato dalla stragrande maggioranza dei professionisti della sicurezza.


Questo è tutto carino e dandy, ma non affronta affatto la domanda come da titolo.
TomTom,

1
@ TomTom: cosa intendi? La domanda è esattamente sul perché le query con parametri sono il meccanismo preferito per prevenire l'iniezione SQL; la mia risposta spiega perché le query con parametri sono più sicure e robuste rispetto alla sanificazione dell'input dell'utente.
Daniel Pryden,

Mi dispiace, ma la MIA domanda recita "Perché il meccanismo di prevenzione dell'iniezione SQL si è evoluto nella direzione dell'utilizzo delle query con parametri?". Non lo fecero. Non si tratta dell'ora, si tratta della storia.
TomTom,

0

Scrivo di questo qui: https://stackoverflow.com/questions/6786034/can-parameterized-statement-stop-all-sql-injection/33033576#33033576

Ma, per dirla in parole semplici:

Il modo in cui funzionano le query con parametri è che sqlQuery viene inviato come query e il database sa esattamente cosa farà questa query, e solo allora inserirà il nome utente e le password semplicemente come valori. Ciò significa che non possono effettuare la query, poiché il database sa già cosa farà la query. Quindi in questo caso cercherebbe un nome utente di "Nessuno OR 1 = 1 '-" e una password vuota, che dovrebbe apparire falsa.

Tuttavia, questa non è una soluzione completa e la convalida dell'input dovrà ancora essere eseguita, poiché ciò non influirà su altri problemi, come gli attacchi XSS, poiché potresti comunque inserire javascript nel database. Quindi, se questo viene letto su una pagina, lo visualizzerebbe come normale javascript, a seconda di qualsiasi convalida dell'output. Quindi la cosa migliore da fare è utilizzare la convalida dell'input, ma utilizzare query con parametri o stored procedure per arrestare eventuali attacchi SQL


0

Non ho mai usato SQL. Ma ovviamente senti quali problemi hanno le persone e gli sviluppatori SQL hanno avuto problemi con questa cosa "iniezione SQL". Per molto tempo non sono riuscito a capirlo. E poi ho capito che le persone creavano istruzioni SQL, istruzioni di testo SQL testuali reali, concatenando stringhe, di cui alcune erano inserite da un utente. E il mio primo pensiero su quella realizzazione è stato uno shock. Shock totale. Ho pensato: come si può essere così ridicolmente stupidi e creare dichiarazioni in un linguaggio di programmazione del genere? Per uno sviluppatore C, C ++, Java o Swift, questa è una vera follia.

Detto questo, non è molto difficile scrivere una funzione C che accetta una stringa C come argomento e produce una stringa diversa che assomiglia esattamente a una stringa letterale nel codice sorgente C che rappresenta la stessa stringa. Ad esempio, quella funzione tradurrebbe abc in "abc" e "abc" in "\" abc \ "" e "\" abc \ "" in "\" \\ "abc \\" \ "". (Beh, se questo ti sembra sbagliato, è html. Era giusto quando l'ho digitato, ma non quando viene visualizzato) E una volta scritta quella funzione C, non è affatto difficile generare codice sorgente C dove il testo da un campo di input fornito dall'utente viene trasformato in una stringa C letterale. Non è difficile renderlo sicuro. Perché gli sviluppatori SQL non userebbero questo approccio come modo per evitare iniezioni di SQL è al di là di me.

"Sanificazione" è un approccio totalmente imperfetto. Il difetto fatale è che rende illegali determinati input dell'utente. Si finisce con un database in cui un campo di testo generico non può contenere testo simile; Eliminare la tabella o qualsiasi altra cosa si utilizzi in un'iniezione SQL per causare danni. Lo trovo abbastanza inaccettabile. Se un database memorizza del testo, dovrebbe essere in grado di memorizzare qualsiasi testo. E il difetto pratico è che il disinfettante non sembra riuscire a farlo bene :-(

Naturalmente, le query con parametri sono quelle che ogni programmatore che utilizza un linguaggio compilato si aspetterebbe. Rende la vita molto più semplice: hai qualche input di stringa e non ti preoccupi nemmeno di tradurlo in una stringa SQL, ma lo passi semplicemente come parametro, senza alcuna possibilità che nessun carattere in quella stringa causi danni.

Quindi dal punto di vista di uno sviluppatore che utilizza linguaggi compilati, la sanificazione è qualcosa che non mi verrebbe mai in mente. La necessità di disinfettare è folle. Le query con parametri sono la soluzione ovvia al problema.

(Ho trovato interessante la risposta di Josip. Fondamentalmente dice che con le query con parametri puoi fermare qualsiasi attacco contro SQL, ma poi puoi avere del testo nel tuo database che viene usato per creare un'iniezione JavaScript :-( Bene, abbiamo di nuovo lo stesso problema e non so se Javascript abbia una soluzione a questo.


-2

Il problema principale è che gli hacker hanno trovato il modo di circondare i servizi igienico-sanitari mentre le query parametrizzate erano una procedura esistente che funzionava perfettamente con i vantaggi aggiuntivi di prestazioni e memoria.

Alcune persone semplificano il problema in quanto "è solo la virgoletta singola e la virgoletta doppia", ma gli hacker hanno trovato modi intelligenti per evitare il rilevamento come l'utilizzo di codifiche diverse o l'utilizzo di funzioni di database.

Ad ogni modo, hai solo bisogno di dimenticare una singola stringa per creare una catastrofica violazione dei dati. Gli hacker erano in grado di automatizzare gli script per scaricare l'intero database con una serie o query. Se il software è ben noto come una suite open source o una famosa suite business, puoi semplicemente attirare la tabella degli utenti e delle password.

D'altra parte, usare solo query concatenate era solo una questione di apprendimento e di abituarsi.

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.