クエリのような安全なActiveRecord


89

LIKEクエリを作成しようとしています。

純粋な文字列のクエリは安全ではないことを読みましたが、安全なLIKEハッシュクエリの記述方法を説明するドキュメントは見つかりませんでした。

出来ますか?SQLインジェクションに対して手動で防御する必要がありますか?


回答:


168

クエリ文字列が適切にサニタイズされるようにするには、配列またはハッシュクエリ構文を使用して条件を記述します。

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

または:

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

キャラクターqueryが含まれている可能性がある場合は、最初%に消毒する必要があります。querysanitize_sql_like

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

これ%はクエリ文字列でエスケープできません。これは恣意的な「SQLインジェクション」ではありませんが、それでも予期せず機能する可能性があります。
Beni Cherniavsky-Paskin 2017年

@ BeniCherniavsky-Paskin:それが要点です。これは構文の一部である%ため、エスケープしたくありません。エスケープした場合、結果は基本的に通常のクエリになります。%LIKE%=
spickermann 2017年

2
そうです、パターンテンプレートで%ワイルドカードを使用したいのですが、そのパターンは変数でパラメーター化されてqueryおり、多くの場合、query変数の文字列と文字通り一致させ、queryLIKEメタ文字の使用を許可しません。%...%というより現実的な例を見てみましょう。文字列はパスのような構造を持っており、を一致させようとします/users/#{user.name}/tags/%。これで、ユーザー名をに設定するとfr%d%、タグを監視できるようにfredなりfridaます...
Beni Cherniavsky-Paskin 2017年

2
OK、私が求めているのは、この質問をstackoverflow.com/questions/5709887/…と組み合わせることsanitize_sql_like()です。
Beni Cherniavsky-Paskin 2017年

2
@ BeniCherniavsky-Paskin今、私はあなたがどこから来たのか理解しました、そしてあなたは正しいです。その問題に対処するために回答を更新しました。
spickermann 2017年

36

Arelを使用すると、次の安全でポータブルなクエリを実行できます。

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

1
Arelはsql-dbに依存せず、内部入力クレンジングがあるため、これが望ましいソリューションです。また、コードスタイルに関する限り、はるかに読みやすく、一貫性があります、IMHO。
アンドリュームーア

これをどのように否定しますか?(すなわち、NOT LIKE)がModel.where(title.matches("%#{query}%").not)生成されたSQLは少しぎこちないですが、動作します:WHERE (NOT (`models`.`title` LIKE '%foo%'))
Noach Magedman

ああ...それを見つけた。 Model.where(title.does_not_match("%#{query}%"))。生成: WHERE (`models`.`title` NOT LIKE '%foo%')
Noach Magedman 2018

慎重に-これはサニタイズに失敗%:信頼できない入力に >> 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またはModel.where.not(title.matches("%#{query}%"))does_not_matchでも、IMOの方が読みやすいです。
elquimista



1

ネストされた関連付けで検索クエリを実行している人がいる場合は、次のことを試してください。

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

複数の属性については、これを試してください。

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

AssociatedModelNameモデル名に置き換えることを忘れないでください

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.