Posso proteggere dall'iniezione SQL sfuggendo a virgolette singole e all'input dell'utente circostante con virgolette singole?


140

Mi rendo conto che le query SQL con parametri sono il modo ottimale per disinfettare l'input dell'utente quando si creano query che contengono input dell'utente, ma mi chiedo cosa c'è di sbagliato nel prendere l'input dell'utente e sfuggire a qualsiasi virgoletta singola e circondare l'intera stringa con virgolette singole. Ecco il codice:

sSanitizedInput = "'" & Replace(sInput, "'", "''") & "'"

Qualsiasi virgoletta singola immessa dall'utente viene sostituita da virgolette singole doppie, il che elimina la possibilità per gli utenti di terminare la stringa, quindi qualsiasi altra cosa che possano digitare, come punti e virgola, segni di percentuale, ecc., Farà parte della stringa e non effettivamente eseguito come parte del comando.

Stiamo usando Microsoft SQL Server 2000, per il quale credo che la virgoletta singola sia l'unico delimitatore di stringa e l'unico modo per sfuggire al delimitatore di stringa, quindi non c'è modo di eseguire nulla in cui l'utente digita.

Non vedo alcun modo per lanciare un attacco di iniezione SQL contro questo, ma mi rendo conto che se fosse a prova di proiettile come sembra a me qualcun altro ci avrebbe già pensato e sarebbe pratica comune.

Cosa c'è che non va in questo codice? C'è un modo per ottenere un attacco di iniezione SQL oltre questa tecnica di sanificazione? Un input utente di esempio che sfrutta questa tecnica sarebbe molto utile.


AGGIORNARE:

Non conosco ancora alcun modo per lanciare efficacemente un attacco di iniezione SQL contro questo codice. Alcune persone hanno suggerito che una barra rovesciata sfuggirebbe a una virgoletta singola e lascerebbe l'altra per terminare la stringa in modo che il resto della stringa venga eseguito come parte del comando SQL, e mi rendo conto che questo metodo funzionerebbe per iniettare SQL in un database MySQL, ma in SQL Server 2000 l'unico modo (che sono stato in grado di trovare) di sfuggire a una virgoletta singola è con un'altra virgoletta singola; le barre rovesciate non lo faranno.

E a meno che non ci sia un modo per fermare l'escaping della virgoletta singola, nessuno degli altri input dell'utente verrà eseguito perché tutto verrà preso come una stringa contigua.

Capisco che ci sono modi migliori per disinfettare l'input, ma sono davvero più interessato a capire perché il metodo che ho fornito sopra non funzionerà. Se qualcuno conosce un modo specifico per montare un attacco di iniezione SQL contro questo metodo di sanificazione mi piacerebbe vederlo.


17
@BryanH Ammettere di non capire come la saggezza comunemente accettata si applica a un caso specifico e chiedere un esempio su tale caso specifico non è arroganza, è umiltà. Infastidirsi quando qualcuno chiede un esempio del perché la saggezza comunemente accettata è d'altra parte potrebbe sembrare arrogante. Ragionare su esempi specifici è spesso un ottimo modo per investigare e apprendere. Il modo in cui l'OP ha risolto questo dubbio è stato molto utile per la mia comprensione dell'argomento, specialmente quando ha spiegato la risposta che ha trovato.
SantiBailors,

@patrik Mi sono appena imbattuto in questo, mentre sto lavorando sullo stesso pezzo di codice ma sto cercando di sfuggire alla stringa e nidificare una query. L'hai mai capito?
3therk1ll

1
@ 3therk1ll è meglio non provarci, stai meglio usando SQL parametrizzato: blog.codinghorror.com/…
Patrick

@Patrick, mi sto avvicinando dal punto di vista degli attaccanti!
3therk1ll,

Risposte:


88

Prima di tutto, è solo una cattiva pratica. La convalida dell'input è sempre necessaria, ma è anche sempre iffy.
Peggio ancora, la convalida della lista nera è sempre problematica, è molto meglio definire esplicitamente e rigorosamente quali valori / formati si accettano. Certo, questo non è sempre possibile - ma in una certa misura deve sempre essere fatto.
Alcuni articoli di ricerca sull'argomento:

Il punto è che qualsiasi lista nera che fai (e whitelist troppo permissive) può essere esclusa. L'ultimo link al mio documento mostra le situazioni in cui è possibile ignorare anche la fuga di virgolette.

Anche se queste situazioni non ti riguardano, è comunque una cattiva idea. Inoltre, a meno che la tua app non sia banalmente piccola, dovrai occuparti della manutenzione e forse di un certo grado di governance: come assicurarti che sia fatto bene, sempre e ovunque?

Il modo corretto di farlo:

  • Convalida lista bianca: tipo, lunghezza, formato o valori accettati
  • Se vuoi inserire nella blacklist, vai avanti. La citazione dell'evasione è buona, ma nel contesto delle altre mitigazioni.
  • Utilizzare gli oggetti Command e Parameter per eseguire l'anteprima e la convalida
  • Chiamare solo query con parametri.
  • Meglio ancora, utilizzare esclusivamente Stored procedure.
  • Evitare l'uso di SQL dinamico e non utilizzare la concatenazione di stringhe per creare query.
  • Se si utilizzano SP, è anche possibile limitare le autorizzazioni nel database all'esecuzione dei soli SP necessari e non accedere direttamente alle tabelle.
  • puoi anche verificare facilmente che l'intero codebase acceda al DB solo tramite SP ...

2
Se utilizzato correttamente, la concatenazione dinamica di stringhe e SQL può essere utilizzata in modo sicuro con query con parametri (ovvero con sp_executesqlanziché anziché EXEC). Cioè, puoi generare dinamicamente la tua istruzione SQL purché nessuno del testo concatenato provenga dall'utente. Ciò ha anche vantaggi in termini di prestazioni; sp_executesqlsupporta la memorizzazione nella cache.
Brian,

2
@Brian, beh duh :). Ma in realtà, quante volte vedi che i programmatori lo fanno? Inoltre, lo scenario tipico in cui è "necessario" l'SQL dinamico, richiede l'input dell'utente come parte della query (presumibilmente). Se potessi fare sp_executesql, in primo luogo non avresti (di solito) bisogno di sql dinamico.
AviD,

Alla fine mi sono imbattuto in una situazione che mi ha fatto capire che è possibile usare unicode per passare oltre la sostituzione della stringa. Il testo di input è stato digitato in Word, che ha cambiato l'apostrofo dalla versione diretta in un apostrofo "riccio" (che assomiglia più a una virgola), che non è stato influenzato dalla sostituzione della stringa ma è stato trattato come delimitatore di stringa da SQL Server. Grazie per la risposta AviD (e tutti gli altri)!
Patrick,

1
@ElRonnoco certo, ma non lo sconto, visto che l'ho visto in natura più volte di quanto pensi ...
AviD

1
@AviD Ho aggiornato il link al PDF di contrabbando di SQL che hai scritto sull'unica versione che ho trovato online ... facci sapere se c'è un'altra posizione per il tuo documento.
Michael Fredrickson,

41

Va bene, questa risposta riguarderà l'aggiornamento della domanda:

"Se qualcuno conosce un modo specifico per montare un attacco di iniezione SQL contro questo metodo di sanificazione, mi piacerebbe vederlo."

Ora, oltre alla fuga di backslash di MySQL - e tenendo conto del fatto che stiamo effettivamente parlando di MSSQL, in realtà ci sono 3 modi possibili per iniettare ancora codice SQL

sSanitizedInput = "'" & Replace (sInput, "'", "''") & "'"

Tieni presente che questi non saranno tutti sempre validi e dipendono molto dal tuo codice attuale:

  1. Iniezione SQL del secondo ordine: se una query SQL viene ricostruita in base ai dati recuperati dal database dopo l'escape , i dati vengono concatenati senza escape e possono essere indirettamente iniettati in SQL. Vedere
  2. Troncamento delle stringhe - (un po 'più complicato) - Lo scenario è che hai due campi, come un nome utente e una password, e l'SQL li concatena entrambi. E entrambi i campi (o solo il primo) hanno un limite rigido per la lunghezza. Ad esempio, il nome utente è limitato a 20 caratteri. Supponi di avere questo codice:
username = left(Replace(sInput, "'", "''"), 20)

Quindi quello che ottieni - è il nome utente, fuggito e quindi ridotto a 20 caratteri. Il problema qui - inserirò la mia citazione nel 20 ° personaggio (ad es. Dopo 19 a) e la tua citazione in fuga verrà ritagliata (nel 21 ° carattere). Quindi l'SQL

sSQL = "select * from USERS where username = '" + username + "'  and password = '" + password + "'"

combinato con il suddetto nome utente non valido comporterà che la password è già al di fuori delle virgolette e conterrà semplicemente il payload direttamente.
3. Il contrabbando di Unicode - In alcune situazioni, è possibile passare un carattere Unicode di alto livello che sembra una citazione, ma non lo è - fino a quando non arriva al database, dove improvvisamente si trova . Dal momento che non è una citazione quando la convalidi, passerà attraverso facile ... Vedi la mia risposta precedente per maggiori dettagli e link alla ricerca originale.


28

In poche parole: non eseguire mai query di escape te stesso. Sei destinato a sbagliare qualcosa. Utilizza invece query con parametri o, se per qualche motivo non riesci a farlo, utilizza una libreria esistente che lo fa per te. Non c'è motivo di farlo da soli.


2
E se dovessi avere a che fare con qualcosa come "Tabelle di Google Fusion" in cui, a quanto pare, non esiste alcuna libreria di astrazione disponibile che supporti il ​​suo dialetto? Che cosa suggeriresti?
systempuntoout

20

Mi rendo conto che è passato molto tempo dalla domanda, ma ...

Un modo per lanciare un attacco alla procedura "quote the argomento" è con il troncamento della stringa. Secondo MSDN, in SQL Server 2000 SP4 (e SQL Server 2005 SP1), una stringa troppo lunga verrà troncata silenziosamente.

Quando si cita una stringa, la stringa aumenta di dimensioni. Ogni apostrofo si ripete. Questo può quindi essere usato per spingere parti di SQL all'esterno del buffer. Quindi potresti tagliare efficacemente le parti di una clausola where.

Ciò sarebbe probabilmente utile soprattutto in uno scenario di pagina "user admin" in cui potresti abusare dell'istruzione "update" per non fare tutti i controlli che avrebbe dovuto fare.

Quindi, se decidi di citare tutti gli argomenti, assicurati di sapere cosa succede con le dimensioni della stringa e accertati che non si verifichi il troncamento.

Consiglierei di andare con i parametri. Sempre. Vorrei solo che potessi applicarlo nel database. E come effetto collaterale, è più probabile che tu ottenga migliori riscontri nella cache perché più dichiarazioni sembrano uguali. (Questo era certamente vero su Oracle 8)


1
Dopo la pubblicazione, ho deciso che il post di AviD copre questo, e in modo più dettagliato. Spero che il mio post sia ancora di aiuto a qualcuno.
Jørn Jensen,

10

Ho usato questa tecnica quando ho a che fare con la funzionalità di "ricerca avanzata", in cui la creazione di una query da zero era l'unica risposta possibile. (Esempio: consentire all'utente di cercare prodotti in base a una serie illimitata di vincoli sugli attributi del prodotto, visualizzando colonne e i loro valori consentiti come controlli della GUI per ridurre la soglia di apprendimento per gli utenti.)

Di per sé è sicuro AFAIK. Come ha sottolineato un altro risponditore, tuttavia, potrebbe anche essere necessario gestire l'escaping del backspace (anche se non quando si passa la query a SQL Server utilizzando ADO o ADO.NET, almeno - non è possibile garantire tutti i database o le tecnologie).

Il problema è che devi davvero essere sicuro di quali stringhe contengono input dell'utente (sempre potenzialmente dannoso) e quali stringhe sono query SQL valide. Una delle trappole è se si utilizzano valori dal database: quei valori erano originariamente forniti dall'utente? In tal caso, devono anche essere evasi. La mia risposta è provare a disinfettare il più tardi possibile (ma non più tardi!), Durante la costruzione della query SQL.

Tuttavia, nella maggior parte dei casi, l'associazione dei parametri è la strada da percorrere: è solo più semplice.


2
Puoi comunque utilizzare la sostituzione dei parametri anche se stai creando le tue query.
Nick Johnson,

1
È necessario creare la stringa dell'istruzione SQL da zero, ma utilizzare comunque la sostituzione dei parametri.
JeeBee,

No, MAI costruire le tue istruzioni SQL da zero.
AviD,

8

I servizi igienico-sanitari in ingresso non sono qualcosa che vuoi fare a metà. Usa tutto il culo. Usa espressioni regolari sui campi di testo. TryCast i tuoi numeri al tipo numerico corretto e segnala un errore di convalida se non funziona. È molto facile cercare schemi di attacco nel tuo input, come '-. Supponiamo che tutti gli input dell'utente siano ostili.


4
E quando perdi quel ONE caso su ONE input, sei pwnd.
BryanH,

4
"Alcune persone, di fronte a un problema, pensano" Lo so, userò espressioni regolari. "Ora hanno due problemi".
MickeyfAgain_BeforeExitOfSO il

1
@mickeyf So che questo è un sentimento comune, ma le espressioni onestamente regolari sono davvero fantastiche una volta che le hai prese.
tom.dietrich,

@ tom.dietrich Dipende sempre dalla situazione di vita reale. F.ex. La sintassi di regexpr non è standard, quindi in generale sconsiglio di usare regexpr in contesti in cui sistemi diversi sono integrati per lavorare insieme. Questo perché diversi motori regexpr valutano le regexprs in modo diverso e, cosa più importante, questo fatto difficile viene solitamente minimizzato o ignorato, il che può indurre gli sviluppatori a non preoccuparsi di queste incompatibilità fino a quando non vengono morsi. Ci sono molte di tali incompatibilità; vedi f.ex. regular-expressions.info/shorthand.html (cerca flavorsin quella pagina).
SantiBailors,

6

È una cattiva idea come sembri sapere.

Che dire di qualcosa come sfuggire alla citazione in una stringa come questa: \ '

Il tuo sostituto comporterebbe: \ ''

Se la barra rovesciata sfugge alla prima virgoletta, la seconda virgoletta ha terminato la stringa.


3
Grazie per la risposta! So che l'attacco funzionerebbe per un database mySQL ma sono abbastanza sicuro che MS SQL Server non accetterà una barra rovesciata come carattere di escape (l'ho provato). Diverse ricerche su Google non hanno rivelato altri caratteri di fuga, il che mi ha fatto davvero chiedere perché non avrebbe funzionato.
Patrick,

6

Risposta semplice: funzionerà a volte, ma non sempre. Vuoi utilizzare la validazione della white list su tutto ciò che fai, ma mi rendo conto che non è sempre possibile, quindi sei costretto a scegliere la migliore lista nera delle ipotesi. Allo stesso modo, vuoi usare i proc memorizzati parametrizzati in tutto , ma ancora una volta, questo non è sempre possibile, quindi sei costretto a usare sp_execute con i parametri.

Ci sono modi per aggirare qualsiasi lista nera utilizzabile che puoi inventare (e anche alcune liste bianche).

Una scrittura decente è qui: http://www.owasp.org/index.php/Top_10_2007-A2

Se hai bisogno di farlo come una soluzione rapida per darti il ​​tempo di crearne uno reale, fallo. Ma non pensare di essere al sicuro.


6

Esistono due modi per farlo, senza eccezioni, per essere al sicuro dalle iniezioni di SQL; dichiarazioni preparate o procedure memorizzate prameterizzate.


4

Se sono disponibili query con parametri, è necessario utilizzarle in ogni momento. Basta che una query scivoli attraverso la rete e che il tuo DB sia a rischio.


4

Sì, dovrebbe funzionare fino a quando qualcuno esegue SET QUOTED_IDENTIFIER OFF e utilizza una doppia citazione su di te.

Modifica: non è semplice come non consentire all'utente malintenzionato di disattivare gli identificatori tra virgolette:

Il driver ODBC di SQL Server Native Client e il provider OLE DB di SQL Server Native Client per SQL Server impostano automaticamente QUOTED_IDENTIFIER su ON durante la connessione. Questo può essere configurato nelle origini dati ODBC, negli attributi di connessione ODBC o nelle proprietà di connessione OLE DB. L'impostazione predefinita per SET QUOTED_IDENTIFIER è OFF per le connessioni dalle applicazioni DB-Library.

Quando viene creata una procedura memorizzata, le impostazioni SET QUOTED_IDENTIFIER e SET ANSI_NULLS vengono acquisite e utilizzate per le successive invocazioni della procedura memorizzata .

SET QUOTED_IDENTIFIER corrisponde anche all'impostazione QUOTED_IDENTIFER di ALTER DATABASE.

SET QUOTED_IDENTIFIER è impostato al momento dell'analisi . L'impostazione al momento dell'analisi significa che se l'istruzione SET è presente nel batch o nella procedura memorizzata, ha effetto, indipendentemente dal fatto che l'esecuzione del codice raggiunga effettivamente quel punto; e l'istruzione SET diventa effettiva prima dell'esecuzione di qualsiasi istruzione.

Ci sono molti modi in cui QUOTED_IDENTIFIER potrebbe essere spento senza che tu lo sappia necessariamente. Certo, questa non è l'exploit delle pistole fumanti che stai cercando, ma è una superficie di attacco piuttosto grande. Ovviamente, se anche tu sei sfuggito alle doppie virgolette, allora siamo di nuovo al punto di partenza. ;)


1
Potrebbe funzionare, ma ancora una volta, come potrebbero ottenere quel codice da eseguire quando tutto l'input dell'utente è racchiuso tra virgolette singole? Una riga specifica (s) di codice che sarebbe in grado di iniettare SQL nel codice sopra sarebbe molto utile. Grazie!
Patrick,

4

La tua difesa fallirebbe se:

  • la query prevede un numero anziché una stringa
  • c'era un altro modo per rappresentare un singolo segno di virgolette, tra cui:
    • una sequenza di escape come \ 039
    • un carattere unicode

(in quest'ultimo caso, dovrebbe essere qualcosa che è stato ampliato solo dopo aver effettuato la sostituzione)


4

Patrick, stai aggiungendo virgolette singole attorno a TUTTI gli input, anche quelli numerici? Se si dispone di input numerici, ma non si inseriscono le virgolette singole, si ottiene un'esposizione.


1

Che brutto codice sarebbe tutta quella sanificazione dell'input dell'utente! Quindi il grosso StringBuilder per l'istruzione SQL. Il metodo di istruzione preparato genera un codice molto più pulito e i vantaggi di SQL Injection sono un'aggiunta davvero interessante.

Anche perché reinventare la ruota?


1

Invece di cambiare una singola virgoletta in (come appare) due virgolette singole, perché non cambiarla in un apostrofo, una citazione o rimuoverla del tutto?

Ad ogni modo, è un po 'un kludge ... specialmente quando hai legittimamente cose (come nomi) che possono usare virgolette singole ...

NOTA: il tuo metodo presuppone anche che tutti coloro che lavorano sulla tua app ricordino sempre di disinfettare l'input prima che colpisca il database, il che probabilmente non è realistico per la maggior parte del tempo.


Sotto votato perché la risposta non risponde alla domanda. La domanda riguarda l'escape delle stringhe in SQL. Quando si evita una stringa arbitraria (come sta tentando di fare l'interrogante, al fine di gestire i dati non autorizzati), non è possibile sostituire semplicemente i caratteri problematici con altri arbitrari; che corrompe i dati. (Inoltre, una singola citazione È un apostrofo (almeno in ASCII).)
andrewf

-1

Mentre potresti trovare una soluzione che funziona per le stringhe, per i predicati numerici devi anche assicurarti che passino solo numeri (un semplice controllo è che può essere analizzato come int / double / decimal?).

C'è molto lavoro extra.


-2

Potrebbe funzionare, ma mi sembra un po 'strano. Consiglierei di verificare che ogni stringa sia valida testandola invece con un'espressione regolare.


-3

Sì, puoi, se ...

Dopo aver studiato l'argomento, penso che l'input igienizzato come suggerito sia sicuro, ma solo in base a queste regole:

  1. non si consente mai che i valori di stringa provenienti dagli utenti diventino nient'altro che valori letterali di stringa (vale a dire evitare di dare un'opzione di configurazione: "Immettere qui altri nomi / espressioni di colonne SQL:"). Tipi di valore diversi dalle stringhe (numeri, date, ...): convertili nei loro tipi di dati nativi e fornisci una routine letterale SQL per ogni tipo di dati.

    • Le istruzioni SQL sono problematiche da convalidare
  2. o usi le colonne nvarchar/ nchar(e inserisci il prefisso letterale stringa con N) OPPURE i valori limite che vanno in varchar/ charcolonne solo ai caratteri ASCII (ad esempio, genera un'eccezione durante la creazione dell'istruzione SQL)

    • in questo modo eviterai la conversione automatica dell'apostrofo da CHAR (700) a CHAR (39) (e forse altri hack Unicode simili)
  3. convalidi sempre la lunghezza del valore per adattarla alla lunghezza effettiva della colonna (genera un'eccezione se più lunga)

    • si è verificato un difetto noto in SQL Server che consente di ignorare l'errore SQL generato al troncamento (che porta al troncamento silenzioso)
  4. ti assicuri che lo SET QUOTED_IDENTIFIERsia sempreON

    • attenzione, viene applicato in tempo reale, cioè anche in sezioni inaccessibili di codice

Rispettando questi 4 punti, dovresti essere al sicuro. Se si viola uno di essi, si apre un modo per l'iniezione SQL.


1
È come se non avessi letto tutte le altre risposte a questa domanda di 8 anni , dal momento che un numero qualsiasi di queste risposte indica che il suo metodo non riesce a bloccare l'iniezione se l'attaccante usa semplicemente caratteri unicode.
Hogan,

@Hogan - L'ho fatto, ma penso che ci sia un valore aggiunto nella mia domanda. Ho molta esperienza e test dietro ciò che ho scritto. So che è meglio usare i parametri della query, ma capisco anche pienamente la situazione in cui qualcuno deve evitarlo a causa di vari motivi (ad esempio, il datore di lavoro richiede di mantenere la vecchia maniera). In questo caso, penso che la mia risposta sia molto completa e abbia un valore più elevato rispetto alle risposte che dicono "semplicemente non farlo", perché mostra la strada da percorrere. Mostrami altre risposte qui che mostrano lo stesso modo e prenderò in considerazione la cancellazione delle mie.
miroxlav,

Ok, quando (non se) il tuo sistema viene compromesso, torna indietro ed elimina questa risposta .... oppure potresti utilizzare una query parametrizzata.
Hogan,

@Hogan - Non ho problemi a farlo :) Ma attualmente sostengo che non esiste un modo noto per aggirare questo se si mantengono le 4 regole che ho pubblicato. Se pensi davvero che ci sia un modo per aggirarlo, allora indica solo dove.
miroxlav,

Cattivo consiglio hombre. qualsiasi interpolazione può essere sconfitta.
Shayne,
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.