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?
ruby
activerecord
ruby-on-rails-4
Gal Weiss
fonte
fonte
Respostas:
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
query
podem incluir o%
personagem, então você precisa para higienizarquery
comsanitize_sql_like
primeiro:Foo.where("bar LIKE ?", "%#{sanitize_sql_like(query)}%") Foo.where("bar LIKE :query", query: "%#{sanitize_sql_like(query)}%")
fonte
%
na string de consulta. Não é uma "injeção SQL" arbitrária, mas ainda pode funcionar inesperadamente.%
porque%
é parte daLIKE
sintaxe. Se você escapou do%
, o resultado seria basicamente uma=
consulta normal .query
variável e, em muitos casos, você deseja corresponder literalmente a string naquery
variável, não permitindo oquery
uso 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 serfr%d%
, eu vou ser capaz de observarfred
efrida
's marcas ...sanitize_sql_like()
.Usando o Arel, você pode realizar esta consulta segura e portátil:
title = Model.arel_table[:title] Model.where(title.matches("%#{query}%"))
fonte
Model.where(title.matches("%#{query}%").not)
funciona, embora o SQL gerado seja um pouco estranho:WHERE (NOT (`models`.`title` LIKE '%foo%'))
Model.where(title.does_not_match("%#{query}%"))
. Gera:WHERE (`models`.`title` NOT LIKE '%foo%')
%
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 '%'"
Model.where.not(title.matches("%#{query}%"))
.does_not_match
lê melhor, porém, IMO.Para PostgreSQL será
Foo.where("bar ILIKE ?", "%#{query}%")
fonte
Você pode fazer
MyModel.where(["title LIKE ?", "%#{params[:query]}%"])
fonte
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
AssociatedModelName
pelo nome do seu modelofonte