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

StackOverflow https://stackoverflow.com/questions/139199

Domanda

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 eventuali virgolette singole 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 del stringa e non effettivamente eseguita 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 questo fosse così a prova di proiettile come mi sembra che qualcun altro ci avrebbe già pensato e sarebbe pratica comune. La mia domanda è questa: cosa c'è di sbagliato in questo codice? Qualcuno sa 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.

UPDATE:

Grazie a tutti per le loro risposte; praticamente tutte le informazioni che ho trovato nella mia ricerca sono apparse su questa pagina da qualche parte, il che è un segno dell'intelligenza e dell'abilità delle persone che si sono prese del tempo dalle loro giornate impegnative per aiutarmi con questa domanda.

Il motivo per cui non ho ancora accettato nessuna delle risposte è che non conosco ancora alcun modo per lanciare efficacemente un attacco di iniezione SQL contro questo codice. Alcune persone hanno suggerito che una barra rovesciata sarebbe sfuggita a una virgoletta singola e avrebbe lasciato l'altra per terminare la stringa in modo che il resto della stringa fosse eseguito come parte del comando SQL, e mi rendo conto che questo metodo avrebbe funzionato per iniettare SQL in un database mySQL, ma in MS SQL 2000 l'unico modo (che sono stato in grado di trovare) di sfuggire a una virgoletta singola è con un altro single-qoute; le barre rovesciate non lo faranno. E a meno che non ci sia un modo per fermare l'escaping della virgoletta singola, nessuno del resto dell'input dell'utente verrà eseguito perché tutto sarà preso come una stringa contigua.

Comprendo che esistono modi migliori per disinfettare l'input, ma sono molto 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.

È stato utile?

Soluzione

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 documenti 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 puoi assicurarti che sia fatto bene, sempre e ovunque?

Il modo corretto per 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.
  • Usa gli oggetti Command e Parameter, per pre-analizzare e validare
  • Chiama solo query con parametri.
  • Meglio ancora, utilizzare esclusivamente Stored procedure.
  • Evita 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 ...

Altri suggerimenti

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 SQL nel tuo codice

  

sSanitizedInput = " '" & Amp; Sostituisci (sInput, " '" ;, "' '") & amp; & Quot; '"

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

  1. Iniezione SQL di secondo ordine: se una query SQL viene ricostruita in base ai dati recuperati dal database dopo l'escaping , i dati vengono concatenati senza escape e possono essere indirettamente iniettati in SQL. Vedi
  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, scappato 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à esterna tra 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 come una citazione, ma non è - fino a quando non arriva al database, dove improvvisamente è . Dal momento che non è una citazione quando la convalidi, passerà attraverso ... Vedi la mia risposta precedente per maggiori dettagli e link alla ricerca originale.

In poche parole: non eseguire mai query di escape. 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.

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

Un modo per lanciare un attacco alla procedura "quote the topic" è 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 dell'SQL al di fuori del buffer. Quindi potresti tagliare efficacemente parti di una clausola where.

Questo 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)

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.

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 certo di quali stringhe contengano 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.

È comunque una cattiva idea come sembri sapere.

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

La tua sostituzione comporterebbe: \ ''

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

Risposta semplice: funzionerà a volte, ma non sempre. Desideri utilizzare la convalida della white list su tutto , ma mi rendo conto che non è sempre possibile, quindi sei costretto a scegliere la lista nera migliore. Allo stesso modo, vuoi usare i proc memorizzati parametrizzati in tutto , ma ancora una volta 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 soluzione rapida per darti il ??tempo di metterne uno reale, fallo. Ma non pensare di essere al sicuro.

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

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

Sì, dovrebbe funzionare fino a quando qualcuno non 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 di quella procedura memorizzata .

     

SET QUOTED_IDENTIFIER anche corrisponde 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. ;)

La tua difesa fallirebbe se:

  • la query prevede un numero anziché una stringa
  • c'erano altri modi 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)

Patrick, stai aggiungendo virgolette singole intorno a TUTTI gli input, anche quelli numerici? Se hai un input numerico, ma non ci metti le virgolette singole, allora hai un'esposizione.

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?

Invece di cambiare una singola virgoletta in (come appare) due virgolette singole, perché non cambiarla semplicemente 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.

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?).

È un sacco di lavoro extra.

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

Sì, puoi, se ...

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

  1. non permetti mai che i valori di stringa provenienti dagli utenti diventino nient'altro che valori letterali di stringa (cioè evita di dare l'opzione di configurazione: " Inserisci 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. usi le colonne nvarchar / nchar (e aggiungi il valore letterale stringa con N ) OPPURE i valori limite vanno in varchar / char solo nei caratteri ASCII (ad es. generare 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 sul troncamento (che porta al troncamento silenzioso)
  4. ti assicuri che SET QUOTED_IDENTIFIER sia sempre ON

    • attenzione, viene applicato in tempo reale, ovvero anche in sezioni inaccessibili del codice

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

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top