Safe ActiveRecord como consulta

Respuestas:

166

Para asegurarse de que su cadena de consulta se desinfecte adecuadamente, use la matriz o la sintaxis de consulta hash para describir sus condiciones:

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

o:

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

Si es posible que querypueda incluir el %personaje, primero debe desinfectar querycon sanitize_sql_like:

Foo.where("bar LIKE ?", "%#{sanitize_sql_like(query)}%")
Foo.where("bar LIKE :query", query: "%#{sanitize_sql_like(query)}%")
spickermann
fuente
Esto no logra escapar %en la cadena de consulta. No es una "inyección SQL" arbitraria, pero aún puede funcionar inesperadamente.
Beni Cherniavsky-Paskin
@ BeniCherniavsky-Paskin: Ese es el punto, no quieres escapar %porque %es parte de la LIKEsintaxis. Si escapó %, el resultado sería básicamente una =consulta normal .
spickermann
1
Correcto, USTED quiere usar% comodines en su plantilla de patrón, pero ese patrón está parametrizado con queryvariable, y en muchos casos desea hacer coincidir literalmente la cadena en queryvariable, no permitir el queryuso de metacaracteres LIKE. Tomemos un ejemplo más realista de% ...%: las cadenas tienen una estructura similar a una ruta y usted intenta hacer coincidir /users/#{user.name}/tags/%. Ahora bien, si yo arregle mi nombre de usuario a ser fr%d%, voy a ser capaz de observar fredy frida's etiquetas ...
Beni-Cherniavsky Paskin
2
Bien, lo que busco es combinar esta pregunta con stackoverflow.com/questions/5709887/… que sugiere sanitize_sql_like().
Beni Cherniavsky-Paskin
2
@ BeniCherniavsky-Paskin Ahora entiendo de dónde vienes y tienes razón. Actualicé mi respuesta para abordar ese problema.
spickermann
34

Con Arel puedes realizar esta consulta segura y portátil:

title = Model.arel_table[:title]
Model.where(title.matches("%#{query}%"))
Pedro Rolo
fuente
1
Esta es la solución preferible, ya que Arel es independiente de sql-db y tiene una limpieza de entrada interna. También es mucho más legible y consistente en lo que respecta al estilo del código, en mi humilde opinión.
Andrew Moore
¿Cómo niegas esto? (es decir, NO ME GUSTA) Model.where(title.matches("%#{query}%").not)funciona, aunque el SQL generado es un poco incómodo:WHERE (NOT (`models`.`title` LIKE '%foo%'))
Noach Magedman
Aah ... lo encontré. Model.where(title.does_not_match("%#{query}%")). Genera: WHERE (`models`.`title` NOT LIKE '%foo%')
Noach Magedman
Cuidado - esto no funciona para desinfectar %en la entrada que no se confía: >> 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_matchse lee mejor sin embargo, en mi opinión.
elquimista
7

Para PostgreSQL será

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

Tu puedes hacer

MyModel.where(["title LIKE ?", "%#{params[:query]}%"])
Santhosh
fuente
1
@mikkeljuhl Por favor, mire mi respuesta detenidamente.
Santhosh
0

En caso de que si alguien realiza una consulta de búsqueda en una asociación anidada, intente esto:

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

Para múltiples atributos, intente esto:

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

No olvide reemplazar AssociatedModelNamecon el nombre de su modelo

Aarvy
fuente