2013-12-09 15 views
9

Vedo il commento spesso ripetuto "utilizzare sempre query preparate per la protezione dagli attacchi di SQL injection".Query preparate rispetto alle query create

Qual è la differenza pratica tra l'utilizzo di una query preparata e una query costruita in cui l'input dell'utente è sempre sterilizzato?

Costruita

function quote($value) { 
    global $db; 
    return "'" . mysqli_real_escape_string($db, $value) . "'"; 
} 

$sql = "INSERT INTO foo (a, b) VALUES (" . quote($a) . "," . quote($b) . ")"; 

Predisposta

$stmt = mysqli_prepare($db, "INSERT INTO foo (a, b) VALUES (?, ?)"); 
mysqli_stmt_bind_param($stmt, "ss", $a, $b); 

A parte la verbosità e stile, quali ragioni dovrei utilizzare uno sopra l'altro?

risposta

2

Le query preparate vengono inviate al server SQL separate dai parametri, il che significa che devono essere compilate/ottimizzate solo una volta se eseguite più volte (ad esempio con parametri variabili). Questo può essere abbastanza significativo con dataset di grandi dimensioni.

Oltre a ciò, sono funzionalmente identici a condizione che l'input sia effettivamente correttamente sfuggito in tutti i casi quando si utilizzano query non preparate.

Detto questo, le query preparate sono molto meno soggette a sviste, di solito con conseguente migliore manutenibilità.

Modifica: Controllare anche il commento di @ eggyal sul motivo per cui le istruzioni preparate sono sempre sicure per l'iniezione rispetto ai parametri di query con escape.

+4

Concordato, ma si noti che poiché i parametri delle istruzioni preparate sono * mai * analizzati per SQL dal server, è * impossibile * che SQL venga iniettato attraverso di essi; considerando che i metodi di fuga [sono stati] (http://vigilance.fr/vulnerability/MySQL-SQL-injection-via-multi-byte-characters-5885) (e [rimangono soggetti a] (http://stackoverflow.com/ a/12118602)) vulnerabilità sfruttabili che possono, in alcuni oscuri casi limite, portare ad attacchi di iniezione di successo. – eggyal

+0

@eggyal: esattamente. Questo è il motivo per cui sono funzionalmente identici solo se - di fatto - sono fuggiti correttamente, come affermato. Farlo correttamente non è di solito un compito facile, però. – jwueller

+0

E qui ho pensato che PDO avrebbe salvato tutti noi @eggyal come leggendo http://stackoverflow.com/a/12118602 (Sotto ** The Bad **) –

7

Una domanda eccellente.

Escaping

Escaping è il modo più tradizionale di fare le cose. Aggiunge i backslash a tutti i dati non sicuri, quindi se qualcuno tenta di passare un'istruzione SQL diretta, passerà in modo innocuo invece di essere preso alla lettera. Il problema è che ci sono alcuni casi limite in cui un utente malintenzionato potrebbe ancora aggirare la funzione di fuga. La stragrande maggioranza di questi coinvolge trucchi arcani come cambiare set di caratteri e così via. La cosa principale da capire, se si cerca di scappare, è che non è sicuro scommettere sul fuoco per sicurezza in tutti i casi.

prepared statement

dichiarazioni preparati non sono soggetti agli stessi difetti perché si sta rompendo la query in due parti. La prima parte contiene la query e la seconda contiene i parametri per quella query. Di conseguenza, MySQL può trattare i parametri in modo diverso, in un modo sicuro che non consente l'iniezione (almeno nessuno ha ancora trovato un modo).

Se è necessario eseguire la stessa query più e più volte con valori diversi, le istruzioni preparate consentono di risparmiare molto tempo. MySQL memorizza l'istruzione in memoria in modo che l'esecuzione multipla sia molto veloce.

Non è tuttavia possibile parametrizzare alcune parti della query (ovvero i nomi delle tabelle).

caveat emptor dichiarazioni

così preparati sono sicuri al 100%, giusto? Perché non usare solo dichiarazioni preparate per tutto?Abbiamo risolto SQL injection FOREVER! Bene, non proprio. Ci sono alcuni avvertimenti che devi sapere prima.

Il primo è specifico per mysqli. Ti consigliamo di installare lo MySQL Native Driver (mysqlnd) per eseguire istruzioni preparate che restituiscano risultati in mysqli. In particolare, mysqli_stmt_get_result, che restituisce un set di risultati come mysqli_query. Alcuni host condivisi e server meno recenti utilizzano ancora le precedenti librerie client MySQL. Se si desidera sapere quale si sta utilizzando, eseguire phpinfo() e cercare il blocco mysqlnd. mysqlnd è una buona idea anche se non stai usando istruzioni preparate perché è stato creato dal team di PHP appositamente per ottenere il massimo da MySQL. Alcune persone non possono cambiare il driver, tuttavia, poiché si tratta di una modifica del livello di configurazione del server.

Il secondo è per coloro che utilizzano PDO (un altro modo per connettersi al database in PHP). Ora, PDO ha un modo molto migliore di gestire istruzioni preparate (ha un pratico sistema di aliasing) e NON richiede mysqlnd per fare istruzioni preparate. L'avvertimento qui è che PDO, per impostazione predefinita, emula solo istruzioni preparate, il che significa che se non lo si configura correttamente, tutto ciò che si sta facendo è usare una libreria che eseguirà l'escape per te. Vedi this page sotto PDO::ATTR_EMULATE_PREPARES per maggiori informazioni.

Il terzo è di gran lunga il più importante e devi prestare molta attenzione qui perché questo potrebbe avere un impatto sul tuo programma se non stai attento. La maggior parte delle persone che sostengono dichiarazioni preparate tendono a trascurare il più grande trabocchetto qui, che si sta facendo più query sul proprio database (ecco lo su come eseguirle tramite la CLI). Ora, per le dichiarazioni INSERT e UPDATE in cui si sta per eseguire la stessa query più e più volte non c'è confronto. Le dichiarazioni preparate ti fanno risparmiare molto tempo sulle loro controparti concatenate. Ma per SELECT, diventa molto meno chiaro. Prendi il mio esempio sopra. È una semplice estrazione di un record tramite il campo autonumber. Ma ci vogliono 2 query per eseguire ora, invece di uno. Se si tratta di un'applicazione interna a basso traffico che potrebbe non essere un grosso problema, ma se quella pagina ottiene 100.000 visualizzazioni al giorno, potresti voler ridurre il sovraccarico in quella pagina. In tal caso, la creazione di una query lineare potrebbe essere una soluzione per te.

Così, in breve

mysqli_real_escape_string ha alcune insidie, ma può essere fatto "abbastanza sicuro" per le query singole fino a quando si capisce quelle insidie ​​e non trattata come l'unica sicurezza tra voi e SQL injection.

mysqli_prepare è SEMPRE sicuro, ma fa anche più query quindi potrebbe non essere altrettanto efficiente per il database e/o il programma. Alcune domande dovrebbero SEMPRE essere preparate, mentre alcune probabilmente non dovrebbero esserlo.

Altre soluzioni possono anche utile, invece di utilizzare entrambe le soluzioni

  • Typecasting (ha alcune limitazioni ma niente che con make per SQL non sicuri)
  • Whitelisting (dove ci si assicura, tramite PHP, quali valori saranno accettabile e consente solo quei valori)

Alla fine, sta a te decidere dove ciascuno è appropriato e dove potrebbe essere meglio usare l'altro.

+1

+1. Risposta molto bella in profondità. – jwueller

+0

Sfortunatamente, nella parte di escape fallisce. Francamente, non ha assolutamente senso in questa parte. –

+0

E anche in "MOLTO TEMPO". –

-2

Buona domanda. Molte persone trascurano davvero la vera differenza.

Ci sono due gravi difetti con "costruzione".

In primo luogo, "la costruzione" di cui si sta parlando è manuale. Significa è aperto a due minacce:

  • rende la formattazione staccabile. Un programmatore intelligente sarebbe sempre tentato di "ottimizzare" il codice ... con conseguente spostamento ripetuto di chiamate quote() da qualche altra parte, in precedenza nel codice, per automatizzare il processo. Il che porterà direttamente al disastro, poiché in un grande progetto c'è una grande possibilità di trascurare una o due variabili e lasciarle non formattate.
  • la formattazione manuale dipende sempre dalle capacità e dall'umore del programmatore. Alcuni faranno tutto bene e altri falliranno. Non è possibile garantire una formattazione corretta nel caso in cui sia manuale.

Eppure il segnaposto garantisce che. Finché la variabile ogni passa alla query tramite segnaposto, puoi considerarti al sicuro. Ecco perché ha detto: "usa sempre le query preparate per evitare attacchi SQL injection", indipendentemente dal fatto che siano istruzioni preparate nativamente o semplicemente emulate. L'idea di utilizzare un segnaposto è importante.

Secondo, La sintassi SQL non è limitata solo alle stringhe. Supponiamo che la tua funzione quote() non funzioni con i parametri della clausola LIMIT. Quindi dovrai escogitare qualcos'altro (o semplicemente lasciar entrare l'iniezione).

Mezzi è necessario disporre di un segnaposto per ogni tipo di letterale SQL. Here is my attempt to accomplish the goal - una libreria che consente di avere segnaposto per tutti i tipi di dati usuali.

Problemi correlati