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