Le istruzioni preparate sono sicure al 100% contro l'iniezione SQL?

73

Le istruzioni preparate in realtà sono sicure al 100% contro l'iniezione SQL, presupponendo che tutti i parametri forniti dall'utente siano passati come parametri di assegnazione della query?

Ogni volta che vedo persone che usano le vecchie funzioni mysql_ su StackOverflow (che è, purtroppo, troppo spesso) in genere dico alle persone che le dichiarazioni preparate sono Chuck Norris (o Jon Skeet) delle misure di sicurezza di SQL injection.

Tuttavia, non ho mai visto alcuna documentazione che dichiari categoricamente "questo è sicuro al 100%" . La mia comprensione di loro è che separano il linguaggio di query e i parametri fino alla porta principale del server, che quindi li considera come entità separate.

Sono corretto in questa ipotesi?

    
posta Polynomial 21.05.2012 - 17:51
fonte

2 risposte

47

Garanzia sicura al 100% dall'iniezione SQL? Non lo prenderò (da me).

In linea di principio, il tuo database (o la libreria nella tua lingua che interagisce con il db) potrebbe implementare istruzioni preparate con parametri associati in un modo non sicuro suscettibile a qualche tipo di attacco avanzato, ad esempio l'utilizzo di buffer overflow o di caratteri con terminazione nulla nelle stringhe fornite dall'utente, ecc. (Si potrebbe sostenere che questi tipi di attacchi non dovrebbero essere chiamati SQL injection in quanto sono fondamentalmente diversi, ma questa è solo semantica).

Non ho mai sentito di nessuno di questi attacchi su istruzioni preparate su database reali sul campo e suggerisco vivamente di utilizzare parametri associati per evitare l'iniezione SQL. Senza parametri vincolati o servizi igienico-sanitari, è banale fare un'iniezione SQL. Con solo misure igieniche, è abbastanza spesso possibile trovare un'oscura scappatoia intorno ai servizi igienico-sanitari.

Con i parametri associati, il piano di esecuzione della query SQL viene calcolato in anticipo senza fare affidamento sull'input dell'utente, il che dovrebbe rendere impossibile l'iniezione SQL (poiché le virgolette inserite, i simboli dei commenti, ecc. vengono inseriti solo nell'istruzione SQL già compilata ).

L'unico argomento contro l'utilizzo di istruzioni preparate è che il database ottimizzi i piani di esecuzione in base alla query effettiva. La maggior parte dei database quando viene fornita la query completa sono abbastanza intelligenti da eseguire un piano di esecuzione ottimale; ad es., se la query restituisce una grande percentuale della tabella, vorrebbe percorrere l'intera tabella per trovare le corrispondenze; mentre se si ottengono solo pochi record si può fare una ricerca basata sugli indici [1] .

EDIT: Rispondendo a due critiche (che sono un po 'troppo lunghe per i commenti):

In primo luogo, come altri hanno notato si, ogni database relazionale che supporta istruzioni preparate e parametri associati non precompila necessariamente l'istruzione preparata senza considerare il valore dei parametri associati. Molti database lo fanno abitualmente, ma è anche possibile che i database guardino i valori dei parametri associati quando capiscono il piano di esecuzione. Questo non è un problema, poiché la struttura dell'istruzione preparata con parametri associati separati, rende facile per il database distinguere in modo pulito l'istruzione SQL (comprese le parole chiave SQL) dai dati nei parametri associati (dove nulla in un parametro associato sarà interpretato come una parola chiave SQL). Ciò non è possibile quando si costruiscono istruzioni SQL dalla concatenazione di stringhe in cui le variabili e le parole chiave SQL vengono intercettate.

In secondo luogo, come sottolinea la altra risposta , utilizzando i parametri associati quando si chiama un'istruzione SQL in un punto in un programma impedisce in modo sicuro l'iniezione SQL quando si effettua quella chiamata di livello superiore. Tuttavia, se nell'applicazione sono presenti vulnerabilità di SQL injection nell'applicazione (ad esempio, nelle funzioni definite dall'utente che sono state archiviate ed eseguite nel database che è stato scritto in modo non sicuro per costruire query SQL mediante concatenazione di stringhe).

Ad esempio, se nella tua applicazione hai scritto pseudo-codice come:

sql_stmt = "SELECT create_new_user(?, ?)"
params = (email_str, hashed_pw_str)
db_conn.execute_with_params(sql_stmt, params)

Non può esserci un'iniezione SQL quando si esegue questa istruzione SQL a livello di applicazione. Tuttavia se la funzione del database definita dall'utente è stata scritta in modo non sicuro (usando la sintassi PL / pgSQL):

CREATE FUNCTION create_new_user(email_str, hashed_pw_str) RETURNS VOID AS
$$
DECLARE
   sql_str TEXT;
BEGIN
     sql_str := 'INSERT INTO users VALUES (' || email_str || ', ' || hashed_pw_str || ');'
     EXECUTE sql_str;
END;
$$
LANGUAGE plpgsql;

quindi saresti vulnerabile agli attacchi SQL injection, perché esegue un'istruzione SQL costruita tramite concatenazione di stringhe che mischia l'istruzione SQL con stringhe contenenti i valori delle variabili definite dall'utente.

Detto questo, a meno che non stiate cercando di essere insicuri (costruendo istruzioni SQL tramite concatenazione di stringhe), sarebbe più naturale scrivere in modo sicuro l'utente definito come:

CREATE FUNCTION create_new_user(email_str, hashed_pw_str) RETURNS VOID AS
$$
BEGIN
     INSERT INTO users VALUES (email_str, hashed_pw_str);
END;
$$
LANGUAGE plpgsql;

Inoltre, se hai davvero sentito la necessità di comporre un'istruzione SQL da una stringa in una funzione definita dall'utente, puoi ancora separare le variabili di dati dall'istruzione SQL allo stesso modo di stored_procedures / parametri associati anche all'interno di una funzione definita dall'utente . Ad esempio in PL / pgSQL :

CREATE FUNCTION create_new_user(email_str, hashed_pw_str) RETURNS VOID AS
$$
DECLARE
   sql_str TEXT;
BEGIN
     sql_str := 'INSERT INTO users VALUES($1, $2)'
     EXECUTE sql_str USING email_str, hashed_pw_str;
END;
$$
LANGUAGE plpgsql;

Quindi, usare le istruzioni preparate è al sicuro dall'iniezione SQL, a patto che non si stiano facendo cose non sicure altrove (cioè costruendo istruzioni SQL mediante concatenazione di stringhe).

    
risposta data 21.05.2012 - 18:31
fonte
23

Sicuro al 100%? Neanche vicino. I parametri associati (preparati in base alle istruzioni o in altro modo) in modo efficace possono prevenire, al 100%, una classe di vulnerabilità di SQL injection (presupponendo l'assenza di bug db e un'implementazione sana). In nessun modo impediscono altre classi. Notare che PostgreSQL (il mio db di scelta) ha un'opzione per associare i parametri ad istruzioni ad hoc che salvano un round trip relativo alle istruzioni preparate se non sono necessarie alcune funzionalità di questi.

Devi capire che molti grandi e complessi database sono programmi in sé stessi. La complessità di questi programmi varia un po 'e l'iniezione SQL è qualcosa che deve essere guardato all'interno delle routine di programmazione. Tali routine includono trigger, funzioni definite dall'utente, stored procedure e simili. Non è sempre ovvio come queste cose interagiscano da un livello di applicazione dato che molti buoni dba forniscono un certo grado di astrazione tra il livello di accesso all'applicazione e il livello di archiviazione.

Con i parametri associati, l'albero delle query viene analizzato, quindi, in PostgreSQL almeno, i dati vengono esaminati per poter pianificare. Il piano è eseguito. Con le dichiarazioni preparate, il piano viene salvato in modo da poter ripetere lo stesso piano con dati diversi ripetutamente (questo può o non può essere quello che vuoi). Ma il punto è che con parametri associati, un parametro non può iniettare nulla nell'albero di analisi. Quindi questa classe di problemi di SQL injection è gestita correttamente.

Ma ora dobbiamo registrare chi scrive cosa su una tabella, quindi aggiungiamo trigger e funzioni definite dall'utente per incapsulare la logica di questi trigger. Questi pongono nuovi problemi. Se si dispone di SQL dinamico in questi, è necessario preoccuparsi dell'iniezione SQL lì. Le tabelle in cui scrivono possono avere trigger propri e così via. Allo stesso modo una chiamata di funzione potrebbe richiamare un'altra query che potrebbe richiamare un'altra chiamata di funzione e così via. Ognuno di questi è pianificato indipendentemente dall'albero principale.

Ciò significa che se eseguo una query con un parametro associato come foo'; drop user postgres; -- , non può implicare direttamente la struttura di query di livello superiore e causare l'aggiunta di un altro comando per eliminare l'utente postgres. Tuttavia, se questa query chiama direttamente un'altra funzione, è possibile che da qualche parte lungo la linea, una funzione sia vulnerabile e l'utente postgres venga eliminato. I parametri associati offrivano la protezione no alle query secondarie. Queste query secondarie devono essere sicure che utilizzino i parametri associati nella misura massima possibile e, dove no, dovranno utilizzare le routine di quotazione appropriate.

La tana del coniglio va in profondità.

BTW per una domanda su Stack Overflow dove questo problema è evidente, vedi link

Anche una versione più problematica (a causa della limitazione delle istruzioni di utilità) su link

    
risposta data 04.11.2013 - 12:27
fonte

Leggi altre domande sui tag