Próbuję napisać zapytanie LIKE.
Czytałem, że wymagania dotyczące czystych ciągów nie są bezpieczne, jednak nie mogłem znaleźć żadnej dokumentacji wyjaśniającej, jak pisać bezpieczne zapytanie LIKE Hash.
Czy to możliwe? Czy powinienem ręcznie bronić się przed iniekcją SQL?
ruby
activerecord
ruby-on-rails-4
Gal Weiss
źródło
źródło
Odpowiedzi:
Aby upewnić się, że ciąg zapytania zostanie prawidłowo oczyszczony, użyj tablicy lub składni zapytania hash do opisania warunków:
Foo.where("bar LIKE ?", "%#{query}%")
lub:
Foo.where("bar LIKE :query", query: "%#{query}%")
Jeśli jest to możliwe, że
query
może zawierać%
znak, a następnie trzeba zdezynfekowaćquery
zsanitize_sql_like
pierwszym:Foo.where("bar LIKE ?", "%#{sanitize_sql_like(query)}%") Foo.where("bar LIKE :query", query: "%#{sanitize_sql_like(query)}%")
źródło
%
w ciągu zapytania. Nie jest to przypadkowe „wstrzyknięcie SQL”, ale nadal może nieoczekiwanie działać.%
ponieważ%
jest częściąLIKE
składni. Jeśli unikniesz znaku,%
wynik będzie w zasadzie zwykłym=
zapytaniem.query
zmienną, aw wielu przypadkach chcesz dosłownie dopasować ciąg wquery
zmiennej, nie pozwalającquery
na użycie metaznaków LIKE. Weźmy bardziej realistyczny przykład, że% ...%: łańcuchy mają strukturę przypominającą ścieżkę, a Ty próbujesz dopasować/users/#{user.name}/tags/%
. Teraz, jeśli ustalę moją nazwę użytkownikafr%d%
, będę mógł obserwowaćfred
ifrida
tagi ...sanitize_sql_like()
.Za pomocą Arel możesz wykonać to bezpieczne i przenośne zapytanie:
title = Model.arel_table[:title] Model.where(title.matches("%#{query}%"))
źródło
Model.where(title.matches("%#{query}%").not)
działa, chociaż wygenerowanyWHERE (NOT (`models`.`title` LIKE '%foo%'))
Model.where(title.does_not_match("%#{query}%"))
. Generuje:WHERE (`models`.`title` NOT LIKE '%foo%')
%
z niezaufanych danych wejściowych:>> 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
czyta się lepiej, IMO.W przypadku PostgreSQL będzie to
Foo.where("bar ILIKE ?", "%#{query}%")
źródło
Możesz to zrobić
MyModel.where(["title LIKE ?", "%#{params[:query]}%"])
źródło
W przypadku, gdy ktoś wykonuje zapytanie wyszukiwania na zagnieżdżonym skojarzeniu, spróbuj tego:
Model.joins(:association).where( Association.arel_table[:attr1].matches("%#{query}%") )
W przypadku wielu atrybutów spróbuj tego:
Model.joins(:association).where( AssociatedModelName.arel_table[:attr1].matches("%#{query}%") .or(AssociatedModelName.arel_table[:attr2].matches("%#{query}%")) .or(AssociatedModelName.arel_table[:attr3].matches("%#{query}%")) )
Nie zapomnij zastąpić
AssociatedModelName
nazwą swojego modeluźródło