ActiveRecord seguro como consulta


Respostas:


168

Para garantir que sua string de consulta seja devidamente limpa, use a matriz ou a sintaxe de consulta hash para descrever suas condições:

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

ou:

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

Se é possível que o querypodem incluir o %personagem, então você precisa para higienizar querycom sanitize_sql_likeprimeiro:

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

Falha ao escapar %na string de consulta. Não é uma "injeção SQL" arbitrária, mas ainda pode funcionar inesperadamente.
Beni Cherniavsky-Paskin

@ BeniCherniavsky-Paskin: Esse é o ponto principal, você não quer escapar do %porque %é parte da LIKEsintaxe. Se você escapou do %, o resultado seria basicamente uma =consulta normal .
spickermann

2
Certo, VOCÊ deseja usar% curingas em seu modelo de padrão, mas esse padrão é parametrizado com queryvariável e, em muitos casos, você deseja corresponder literalmente a string na queryvariável, não permitindo o queryuso de metacaracteres LIKE. Vamos dar um exemplo mais realista de que% ...%: as strings têm uma estrutura semelhante a um caminho e você tenta fazer a correspondência /users/#{user.name}/tags/%. Agora, se eu marcar meu nome de usuário para ser fr%d%, eu vou ser capaz de observar frede frida's marcas ...
Beni Cherniavsky-Paskin

2
OK, o que estou procurando é combinar esta pergunta com stackoverflow.com/questions/5709887/… que sugere sanitize_sql_like().
Beni Cherniavsky-Paskin

2
@ BeniCherniavsky-Paskin Agora eu entendo de onde você vem e você está certo. Eu atualizei minha resposta para resolver esse problema.
spickermann

36

Usando o Arel, você pode realizar esta consulta segura e portátil:

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

1
Esta é a solução preferível, uma vez que Arel é agnóstico em sql-db e tem alguma limpeza de entrada interna. Também é muito mais legível e consistente no que diz respeito ao estilo de código, IMHO.
Andrew Moore

Como você nega isso? (ou seja, NOT LIKE) Model.where(title.matches("%#{query}%").not)funciona, embora o SQL gerado seja um pouco estranho:WHERE (NOT (`models`.`title` LIKE '%foo%'))
Noach Magedman

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

Cuidado - falha ao limpar a %entrada não confiável: >> 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 ou Model.where.not(title.matches("%#{query}%")). does_not_matchlê melhor, porém, IMO.
elquimista

7

Para PostgreSQL será

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

1

Você pode fazer

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

1
@mikkeljuhl Por favor, olhe minha resposta com atenção.
Santhosh de

0

No caso de alguém realizar consulta de pesquisa em associação aninhada, tente isto:

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

Para vários atributos, tente o seguinte:

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

Não se esqueça de substituir AssociatedModelNamepelo nome do seu modelo

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.