2010-05-16 7 views
10

Sto facendo una query di Doctrine e devo fare una corrispondenza con caratteri jolly nella clausola where. Come dovrei sfuggire alla variabile che voglio inserire?

La query che voglio ottenere:

SELECT u.* FROM User as u WHERE name LIKE %var% 

Il codice php fino ad ora:

$query = Doctrine_Query::create() 
       ->from('User u') 
       ->where(); 

Cosa dovrebbe venire nella clausola dove? La variabile che voglio abbinare $ nome

risposta

23

risposto correttamente alla tua domanda, quindi farò un pugnalarlo.

->where('u.name LIKE ?', array("%$name%")); 
->where('u.username LIKE ?', '%'.$username.'%') 

Nessuno di questi è sicuro. Lasciatemi spiegare alcuni scenari.

Scenario 1

Immaginate di voler lasciare che gli utenti cercano nomi utente corrispondenti, ma non hai mai voglia di elencare tutti i nomi utente. Forse non vuoi che qualcuno rubi facilmente una lista di milioni di nomi utente da te. da qualche parte prima di questo codice, che hai fatto qualcosa di simile:

if (strlen(trim($name)) < 5) throw Boogey_Monster_Exception(); 

È pensato che questo avrebbe impedito a qualcuno di lasciare il campo vuoto e tirando giù un elenco di tutti i nomi utente ... ma in realtà l'utente può inviare "_____ "o" %%%%% "o qualcosa di simile per ottenere un elenco di tutti i nomi utente, non solo l'abbinamento di 5 o più caratteri noti.

Personalmente ho visto questa forma di attacco utilizzata su diversi grandi siti Web pubblici.

Scenario 2

avete un sito web con un sacco di utenti e un sacco di dati utili. Hai 10.000.000 di righe nella tabella degli utenti. Vuoi consentire agli utenti del sito di trovare il nome utente di un altro utente cercando i prefissi noti.

Così si scrive un codice come questo, leggermente modificato dall'esempio precedente per avere solo un carattere jolly DOPO la stringa di ricerca.

->where('u.name LIKE ?', array("$name%")); 

Se si dispone di un indice su u.name, questa query LIKE utilizzerà l'indice. Quindi, se l'utente invia $ name = "john", allora questa query combacerà in modo efficiente utenti come johndoe, johnwayne, johnwaynegacy, ecc.

Tuttavia, se l'utente invia $ name = "% john", questa query no più a lungo utilizza l'indice e ora richiede una scansione completa della tabella. Su un database molto grande può trattarsi di una query molto lenta.

Il manuale MySQL su SQLi menziona la stessa cosa (pagine 78-79) e ho cercato su google alcuni esempi di prestazioni di query lente e ho trovato un collegamento.

Questo potrebbe non sembrare un grosso problema, ma per i siti supportati da un RDBMS, l'RDBMS è di solito un collo di bottiglia significativo e gran parte dell'ingegneria delle prestazioni ruota attorno alla riduzione della contesa sull'RDBMS. Se hai un gruppetto di utenti che lanciano un attacco che lega un handle di database per 60+ secondi e hai un piccolo pool di handle di database, puoi vedere come questo potrebbe scalare rapidamente per monopolizzare tutti gli handle del database e impedire utenti legittimi dall'essere in grado di averne uno.

Quick

http://dev.mysql.com/tech-resources/articles/guide-to-php-security-ch3.pdf

http://forums.mysql.com/read.php?24,13397,13397

Solution

Comunque, la soluzione migliore (come indicato nel manuale MySQL collegato sopra e commentatore @Maxence, è quello utilizzare addcslashes()):

Si noti che poiché gli esempi SQL qui utilizzano istruzioni preparate, che sono completamente immuni a SQL injection, non è necessario o desiderabile utilizzare mysql_real_escape_string(); la fuga che esegue è esclusivamente per prevenire l'iniezione sql. Quello che stiamo cercando di evitare è l'inserimento di caratteri jolly, e che richiede una funzione che sfugge i due caratteri jolly sql, '%' e '_'.

+1

Interessante, grazie. – Tom

+1

Penso che una buona soluzione per evitare un carico elevato sul DB sia evitare le query LIKE e utilizzare un motore di ricerca come [sphinxs] (http://sphinxsearch.com/). È fantastico! – JeanValjean

+0

@mehaase Ho visto che hai dato -1 ad altre risposte, ma il tuo metodo non è sicuro perché hai dimenticato di sfuggire al personaggio di escape stesso. Quindi, almeno dovrebbe assomigliare a questo: 'addcslashes ('% something_', '\\% _');'. Tieni presente che in MySQL il ''\\ qualcosa' LIKE '\\ qualcosa'' valuta come' FALSE' ma '' \\ qualcosa 'LIKE' \\\\\\\\\\\\\\\\\\\\\\\\\ '' VERO' ;-) – Karolis

-2

successo qualcosa di brutto alla documentazione di Doctrine quindi ecco il Nessuno Google copy (controllare Come Espressioni sezione)

... 
->where('u.name LIKE ?', array("%$name%")); 
+1

Cosa succede se c'è un carattere speciale come '%' o '\ _' in $ nome? Dovresti aggiungere addcslashes ($ name, '% \ _') – Maxence

+0

'addcslashes();'? Non è necessario aggiungere barre a qualsiasi cosa per le query, sia che si utilizzi e sia che si utilizzi ORM o SQL nativo. Doctrine si prende cura di fuggire correttamente senza sporcare i tuoi dati con le barre. Per mysql nativo, usa 'mysql_real_escape_string ($ string);'. Per gli altri, controlla i tuoi documenti; semplicemente non fare 'addcslashes();' – adlawson

+4

@adlawson: Doctrine ** non eseguirà ** escape '%' e '_' per l'espressione' LIKE'. Questo deve essere gestito manualmente. – Crozin