LIKE sorgu yazmaya çalışıyorum.
Saf dizgi sorgularının güvenli olmadığını okudum, ancak Hash Query GİBİ güvenli yazmayı açıklayan herhangi bir belge bulamadım.
Mümkün mü? SQL Injection'a karşı manuel olarak savunma yapmalı mıyım?
LIKE sorgu yazmaya çalışıyorum.
Saf dizgi sorgularının güvenli olmadığını okudum, ancak Hash Query GİBİ güvenli yazmayı açıklayan herhangi bir belge bulamadım.
Mümkün mü? SQL Injection'a karşı manuel olarak savunma yapmalı mıyım?
Yanıtlar:
Sorgu dizenizin düzgün bir şekilde temizlendiğinden emin olmak için, koşullarınızı açıklamak için diziyi veya karma sorgu sözdizimini kullanın:
Foo.where("bar LIKE ?", "%#{query}%")
veya:
Foo.where("bar LIKE :query", query: "%#{query}%")
O mümkünse query
içerebilir %
karakteri o zaman sterilize gerekir query
ile sanitize_sql_like
ilk:
Foo.where("bar LIKE ?", "%#{sanitize_sql_like(query)}%")
Foo.where("bar LIKE :query", query: "%#{sanitize_sql_like(query)}%")
%
, sorgu dizesinde kaçamaz . Bu rastgele bir "SQL enjeksiyonu" değildir, ancak yine de beklenmedik şekilde çalışabilir.
%
çünkü sözdiziminin bir %
parçası LIKE
. Eğer kaçtıysanız, %
sonuç temelde normal bir =
sorgu olacaktır.
query
değişkenle parametreleştirilmiştir ve çoğu durumda query
, query
LIKE meta karakterlerini kullanmaya izin vermemek için değişkendeki dizeyle tam anlamıyla eşleştirmek istersiniz . % ...% 'den daha gerçekçi bir örnek alalım: dizelerin yol benzeri bir yapısı var ve siz eşleştirmeye çalışıyorsunuz /users/#{user.name}/tags/%
. Şimdi kullanıcı adımı olacak şekilde düzenlersem fr%d%
, fred
ve frida
etiketlerini gözlemleyebileceğim ...
sanitize_sql_like()
.
Arel'i kullanarak bu güvenli ve taşınabilir sorguyu gerçekleştirebilirsiniz:
title = Model.arel_table[:title]
Model.where(title.matches("%#{query}%"))
Model.where(title.matches("%#{query}%").not)
çalışıyor, ancak oluşturulan SQL biraz garip olsa da:WHERE (NOT (`models`.`title` LIKE '%foo%'))
Model.where(title.does_not_match("%#{query}%"))
. Oluşturur: WHERE (`models`.`title` NOT LIKE '%foo%')
%
, güvenilmeyen girdileri temizleyemez : >> 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
IMO daha iyi okur.
İç içe geçmiş ilişkilendirmede arama sorgusu yapan biri varsa şunu deneyin:
Model.joins(:association).where(
Association.arel_table[:attr1].matches("%#{query}%")
)
Birden çok özellik için şunu deneyin:
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
Model adınızla değiştirmeyi unutmayın