How to sanitize sql fragment in Rails
Solution 1
You can just use:
ActiveRecord::Base::sanitize_sql(string)
Solution 2
ActiveRecord::Base.connection.quote
does the trick in Rails 3.x
Solution 3
This question does not specify that the answer has to come from ActiveRecord
nor does it specify for which version of Rails it should be. For that reason (and because it is one of the top and few) answers on how to sanitize parameters in Rails...
Here a solution that works with Rails 4:
In ActiveRecord::Sanitization::ClassMethods
you have sanitize_sql_for_conditions and its two other aliases:
sanitize_conditions and sanitize_sql. The three do literally the exact same thing.
sanitize_sql_for_conditions
Accepts an array, hash, or string of SQL conditions and sanitizes them into a valid SQL fragment for a WHERE clause.
Also in ActiveRecord you have
sanitize_sql_for_assignment
which
Accepts an array, hash, or string of SQL conditions and sanitizes them into a valid SQL fragment for a SET clause.
- The methods above are included in ActiveRecord::Base by default and therefore are included in any ActiveRecord model.
See docs
Also, however, in ActionController you have ActionController::Parameters
which allows you to
choose which attributes should be whitelisted for mass updating and thus prevent accidentally exposing that which shouldn't be exposed. Provides two methods for this purpose: require and permit.
params = ActionController::Parameters.new(user: { name: 'Bryan', age: 21 })
req = params.require(:user) # will throw exception if user not present
opt = params.permit(:name) # name parameter is optional, returns nil if not present
user = params.require(:user).permit(:name, :age) # user hash is required while `name` and `age` keys are optional
The "Parameters magic" is called Strong Parameters (docs here) and you can use that to sanitize parameters in a controller before sending it to a model.
- The methods above are included by default in
ActionController::Base
and therefore are included in any Rails controller.
I hope that helps anyone, if only to learn and demystify Rails! :)
Solution 4
As of rails 5 the recomended way is to use: ActiveRecord::Base.connection.quote(string)
as stated here: https://github.com/rails/rails/issues/28947
ActiveRecord::Base::sanitize(string)
is deprecated
Solution 5
Note that when it comes to sanitizing SQL WHERE conditions, the best solution was sanitize_sql_hash_for_conditions, because it correctly handled NULL conditions (e.g. would generate IS NULL
instead of = NULL
if a nil attribute was passed).
For some reason, it was deprecated in Rails 5. So I rolled a future-proofed version, see here: https://stackoverflow.com/a/53948665/165673
Related videos on Youtube
Comments
-
user2308001 about 2 years
I have to sanitize a part of sql query. I can do something like this:
class << ActiveRecord::Base public :sanitize_sql end str = ActiveRecord::Base.sanitize_sql(["AND column1 = ?", "two's"], '')
But it is not safe because I expose protected method. What is a better way to do it?
-
pilcrow almost 14 yearsCan you give us a little more context?
sanitize_sql
and friends are often called inside AR::Base-derived classes, without needing to alter visibility -
user2308001 almost 14 yearsThat is a good and valid point. I just cringe when I use someone's private or protected methods.
-
-
Daniel Rikowski almost 9 yearsThis delegates to
ActiveRecord::Base.connection.quote
(at least in Rails 4) -
Matt over 7 yearsDoes
require
andpermit
sanitize parameters against SQL injection, or just validate their presence? -
Jimmy over 7 yearsit does zero sanitizing, just validates presence
-
Bryan Dimas over 7 years@Matt: Jimmy is correct,
require
andpermit
don't do any sanitizing by themselves. ButActionController::Parameters.new
does the sanitizing, so all of your controllers should already be sanitizing all parameters. I'll update my answer later when I have time because I also found this very cool gem called rails-html-sanitizer github.com/rails/rails-html-sanitizer -
Matt over 7 yearsThanks Bryan. I'm in a situation where I'm sending a large amount of JSON to my controller, and am dealing with 3s+ response times. I was able to decrease these to 2s by eliminating redundancy in my JSON, and further get it down to below 500ms by not instantiating an ActiveRecord object. Just wanted to be sure that I was exposing a security risk!
-
Yarin over 5 years
-
Yarin over 5 yearsNote that the sanitization methods above are protected class methods, so you'll either need to call them from within your AR class or expose them via a public class method in your own AR class.
-
Yarin over 5 yearsNot the recommended. Use the sanitation apis in Bryan's answer instead.
-
Yarin over 5 yearsNone of the current crop of sanitization methods correctly handle nil attributes as WHERE conditions. See here
-
Jason Swett about 4 yearsI updated this answer from the deprecated version (
sanitize
) to the current working version as of Rails 6 (sanitize_sql
). If someone wants a full explanation of all the sanitization methods, I'd suggest the docs.