Bezpieczne ActiveRecord, takie jak zapytanie


Odpowiedzi:


166

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 querymoże zawierać %znak, a następnie trzeba zdezynfekować queryz sanitize_sql_likepierwszym:

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

Nie udaje się to zmienić %w ciągu zapytania. Nie jest to przypadkowe „wstrzyknięcie SQL”, ale nadal może nieoczekiwanie działać.
Beni Cherniavsky-Paskin

@ BeniCherniavsky-Paskin: O to chodzi, nie chcesz uciec od tego, %ponieważ %jest częścią LIKEskładni. Jeśli unikniesz znaku, %wynik będzie w zasadzie zwykłym =zapytaniem.
spickermann

1
Racja, TY chcesz użyć symboli wieloznacznych% w swoim szablonie wzorca, ale ten wzorzec jest sparametryzowany queryzmienną, aw wielu przypadkach chcesz dosłownie dopasować ciąg w queryzmiennej, nie pozwalając queryna 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żytkownika fr%d%, będę mógł obserwować fredi fridatagi ...
Beni Cherniavsky-Paskin

2
OK, szukam połączenia tego pytania ze stackoverflow.com/questions/5709887/… co sugeruje sanitize_sql_like().
Beni Cherniavsky-Paskin

2
@ BeniCherniavsky-Paskin Teraz rozumiem, skąd pochodzisz i masz rację. Zaktualizowałem odpowiedź, aby rozwiązać ten problem.
spickermann

34

Za pomocą Arel możesz wykonać to bezpieczne i przenośne zapytanie:

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

1
Jest to preferowane rozwiązanie, ponieważ Arel jest agnostyczny względem sql-db-agnostic i ma pewne wewnętrzne czyszczenie danych wejściowych. Jest również znacznie bardziej czytelny i spójny, jeśli chodzi o styl kodu, IMHO.
Andrew Moore

Jak to negujesz? (tj. NIE LUBIĘ) Model.where(title.matches("%#{query}%").not)działa, chociaż wygenerowany WHERE (NOT (`models`.`title` LIKE '%foo%'))
kod

Ach ... znalazłem to. Model.where(title.does_not_match("%#{query}%")). Generuje: WHERE (`models`.`title` NOT LIKE '%foo%')
Noach Magedman,

Ostrożnie - nie udaje się to oczyścić %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 '%'"
vjt

@NoachMagedman lub Model.where.not(title.matches("%#{query}%")). does_not_matchczyta się lepiej, IMO.
elquimista,

7

W przypadku PostgreSQL będzie to

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

1

Możesz to zrobić

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

1
@mikkeljuhl Proszę uważnie przyjrzeć się mojej odpowiedzi.
Santhosh

0

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ć AssociatedModelNamenazwą swojego modelu

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.