Sichere ActiveRecord-ähnliche Abfrage

84

Ich versuche, eine ähnliche Abfrage zu schreiben.

Ich habe gelesen, dass reine String-Anforderungen nicht sicher sind, konnte jedoch keine Dokumentation finden, die erklärt, wie eine sichere LIKE Hash-Abfrage geschrieben wird.

Ist es möglich? Sollte ich mich manuell gegen SQL Injection verteidigen?

Gal Weiss
quelle

Antworten:

165

Verwenden Sie das Array oder die Hash-Abfragesyntax, um sicherzustellen, dass Ihre Abfragezeichenfolge ordnungsgemäß bereinigt wird, um Ihre Bedingungen zu beschreiben:

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

oder:

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

Wenn es möglich ist , dass die querydie Maßnahmen enthalten können %Zeichen , dann müssen Sie sanitize querymit sanitize_sql_likeersten:

Foo.where("bar LIKE ?", "%#{sanitize_sql_like(query)}%")
Foo.where("bar LIKE :query", query: "%#{sanitize_sql_like(query)}%")
Spickermann
quelle
Dies kann %in der Abfragezeichenfolge nicht maskiert werden. Es ist keine willkürliche "SQL-Injection", kann aber dennoch unerwartet funktionieren.
Beni Cherniavsky-Paskin
@ BeniCherniavsky-Paskin: Das ist der springende Punkt, du willst dem nicht entkommen, %weil das %Teil der LIKESyntax ist. Wenn Sie dem entkommen sind, %wäre das Ergebnis im Grunde eine normale =Abfrage.
Spickermann
1
Richtig, SIE möchten% Platzhalter in Ihrer Mustervorlage verwenden, aber dieses Muster ist mit queryVariable parametrisiert , und in vielen Fällen möchten Sie buchstäblich mit der Zeichenfolge in queryVariable übereinstimmen und dürfen keine queryLIKE-Metazeichen verwenden. Nehmen wir ein realistischeres Beispiel:% ...%: Die Zeichenfolgen haben eine pfadartige Struktur, und Sie versuchen, eine Übereinstimmung zu erzielen /users/#{user.name}/tags/%. Wenn ich jetzt meinen Benutzernamen einstelle, kann fr%d%ich die Tags von fredund beobachten frida...
Beni Cherniavsky-Paskin
2
OK, ich möchte diese Frage mit stackoverflow.com/questions/5709887/… kombinieren, was darauf hindeutet sanitize_sql_like().
Beni Cherniavsky-Paskin
2
@ BeniCherniavsky-Paskin Jetzt verstehe ich, woher du kommst und du hast recht. Ich habe meine Antwort aktualisiert, um dieses Problem zu beheben.
Spickermann
34

Mit Arel können Sie diese sichere und tragbare Abfrage durchführen:

title = Model.arel_table[:title]
Model.where(title.matches("%#{query}%"))
Pedro Rolo
quelle
1
Dies ist die bevorzugte Lösung, da Arel sql-db-agnostisch ist und einige interne Eingangsbereinigungen aufweist. Auch ist viel besser lesbar und konsistent, was den Codestil betrifft, IMHO.
Andrew Moore
Wie negierst du das? (dh NICHT WIE) Model.where(title.matches("%#{query}%").not)funktioniert, obwohl das generierte SQL etwas umständlich ist:WHERE (NOT (`models`.`title` LIKE '%foo%'))
Noach Magedman
Aah ... habe es gefunden. Model.where(title.does_not_match("%#{query}%")). Erzeugt: WHERE (`models`.`title` NOT LIKE '%foo%')
Noach Magedman
Vorsicht - dies kann nicht %in nicht vertrauenswürdigen Eingaben >> 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 '%'"
bereinigen
@NoachMagedman oder Model.where.not(title.matches("%#{query}%")). does_not_matchliest sich aber besser, IMO.
Elquimista
7

Für PostgreSQL wird es sein

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

Du kannst tun

MyModel.where(["title LIKE ?", "%#{params[:query]}%"])
Santhosh
quelle
1
@mikkeljuhl Bitte schau dir meine Antwort genau an.
Santhosh
0

Falls jemand eine Suchabfrage für eine verschachtelte Zuordnung ausführt, versuchen Sie Folgendes:

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

Versuchen Sie für mehrere Attribute Folgendes:

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

Vergessen Sie nicht, durch AssociatedModelNameIhren Modellnamen zu ersetzen

Aarvy
quelle