ActiveRecord sicuro come query


Risposte:


168

Per assicurarti che la tua stringa di query venga adeguatamente disinfettata, usa l'array o la sintassi della query hash per descrivere le tue condizioni:

Foo.where("bar LIKE ?", "%#{query}%")

o:

Foo.where("bar LIKE :query", query: "%#{query}%")

Se è possibile che querypossa includere il %personaggio, devi prima disinfettare querycon sanitize_sql_like:

Foo.where("bar LIKE ?", "%#{sanitize_sql_like(query)}%")
Foo.where("bar LIKE :query", query: "%#{sanitize_sql_like(query)}%")

Ciò non riesce a sfuggire %alla stringa di query. Non è una "iniezione SQL" arbitraria, ma può comunque funzionare in modo imprevisto.
Beni Cherniavsky-Paskin

@ BeniCherniavsky-Paskin: questo è il punto, non vuoi sfuggire al %perché %fa parte della LIKEsintassi. Se sei sfuggito al, %il risultato sarebbe fondamentalmente una =query normale .
spickermann

2
Bene, TU vuoi usare i caratteri jolly% nel tuo modello di pattern ma quel modello è parametrizzato con la queryvariabile, e in molti casi vuoi abbinare letteralmente la stringa in queryvariabile, non permettere querydi usare i metacaratteri LIKE. Facciamo un esempio più realistico che% ...%: le stringhe hanno una struttura simile a un percorso e tu cerchi di abbinare /users/#{user.name}/tags/%. Ora, se io organizzo il mio nome utente di essere fr%d%, sarò in grado di osservare frede frida's tag ...
Beni Cherniavsky-Paskin

2
OK, quello che sto cercando è combinare questa domanda con stackoverflow.com/questions/5709887/… che suggerisce sanitize_sql_like().
Beni Cherniavsky-Paskin

2
@ BeniCherniavsky-Paskin Ora ho capito da dove vieni e hai ragione. Ho aggiornato la mia risposta per risolvere il problema.
spickermann

36

Usando Arel puoi eseguire questa query sicura e portatile:

title = Model.arel_table[:title]
Model.where(title.matches("%#{query}%"))

1
Questa è la soluzione preferibile, poiché Arel è sql-db-agnostic e ha una pulizia interna dell'input. Inoltre è molto più leggibile e coerente per quanto riguarda lo stile del codice, IMHO.
Andrew Moore

Come lo neghi? (cioè NON MI PIACE) Model.where(title.matches("%#{query}%").not)funziona, anche se l'SQL generato è un po 'imbarazzante:WHERE (NOT (`models`.`title` LIKE '%foo%'))
Noach Magedman,

Aah ... l'ho trovato. Model.where(title.does_not_match("%#{query}%")). Genera: WHERE (`models`.`title` NOT LIKE '%foo%')
Noach Magedman

Attenzione - questo non riesce a disinfettare %in input non attendibili: >> ActiveRecord::VERSION::STRING => "5.2.3" >> field = Foo.arel_table[:bar] >> Foo.where(field.matches('%')).to_sql => "SELECT `foos`.* FROM `foos` WHERE `foos`.`bar` LIKE '%'"
vjt

@NoachMagedman o Model.where.not(title.matches("%#{query}%")). does_not_matchlegge meglio però, IMO.
elquimista

7

Per PostgreSQL lo sarà

Foo.where("bar ILIKE ?", "%#{query}%") 

1

Tu puoi fare

MyModel.where(["title LIKE ?", "%#{params[:query]}%"])

1
@mikkeljuhl Per favore guarda attentamente la mia risposta.
Santhosh

1

Nel caso in cui qualcuno che esegue una query di ricerca sull'associazione nidificata, provi questo:

Model.joins(:association).where(
   Association.arel_table[:attr1].matches("%#{query}%")
)

Per più attributi prova questo:

Model.joins(:association).where(
  AssociatedModelName.arel_table[:attr1].matches("%#{query}%")
    .or(AssociatedModelName.arel_table[:attr2].matches("%#{query}%"))
    .or(AssociatedModelName.arel_table[:attr3].matches("%#{query}%"))
)
 

Non dimenticare di sostituire AssociatedModelNamecon il nome del tuo modello

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.