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?


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)}%")

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}%"))

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}%") 

1

Du kannst tun

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

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

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.