ActiveRecord seguro como consulta

89

Estou tentando escrever uma consulta LIKE.

Eu li que os quires de string puros não são seguros, no entanto, não consegui encontrar nenhuma documentação que explicasse como escrever uma consulta de hash LIKE segura.

É possível? Devo me defender manualmente contra SQL Injection?

Gal Weiss
fonte
Possível duplicata de Como fazer uma consulta LIKE no Arel e Rails?
Pedro Rolo

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)}%")
Spickermann
fonte
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}%"))
Pedro Rolo
fonte
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}%") 
Khoga
fonte
1

Você pode fazer

MyModel.where(["title LIKE ?", "%#{params[:query]}%"])
Santhosh
fonte
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

Aarvy
fonte