Reputation: 613
I am building an API with RoR 4 and PostgreSQL. I want to give my users some search functionality through the index action in my controllers. To this end I built the following function.
query_params is the result of params.permit in one the controllers.
The ouput of this funcion is then sent to the active record where function.
def searchable (query_params)
query = ''
query_params.each do |key, value|
value = value.to_s # Ensure this is a string or you will have crashes below
# Equals any of multiple values
if value[/\A(mul-)/]
value[0..3] = ''
query += "#{key} IN (#{value}) AND "
# Not a single value
elsif value[/\A(<>)/]
value[0..1] = ''
query += "(#{key} <> #{value}) AND "
# Bigger or smaller than a single value
elsif value[/\A[><]/]
operator = value[/\A[><]/]
value[0] = ''
query += "(#{key} #{operator} #{value}) AND "
# Equals a single value
else
query += "#{key} = '#{value}' AND "
end
end
query = query[0..-5] # removes the last AND
end
I am worried this method opens my DB up to sql injections. Am I right? If you have an example of a possible injection that would be great. Finally, is there anything I can do to block injections?
Thanks!
Upvotes: 0
Views: 454
Reputation: 613
After reading @Miguel Cunha's answer I rewrote the code and it is now safe. I wanted to upload the changes, as I think it might be useful for others to compare. In any case I recommend looking at gems (and perhaps Elastic Search) first.
# Permitted params makes sure keys cannot cause SQL injections!
def searchable (query_params)
query_keys = '' # Using array to prevent SQL injection
query_values = {}
query_params.each do |key, value|
value = value.to_s # Ensure this is a string or you will have crashes below
# Equals any of multiple values
if value[/\A(mul-)/]
query_keys += "#{key} IN (:#{key}) AND "
value[0..3] = ''
query_values[key.to_sym] = value.split(',')
# Not a single value
elsif value[/\A(<>)/]
query_keys += "#{key} <> :#{key} AND "
value[0..1] = ''
query_values[key.to_sym] = value
# Bigger or smaller than a single value
elsif value[/\A[><]/]
operator = value[/\A[><]/]
query_keys += "#{key} #{operator} :#{key} AND "
value[0] = ''
query_values[key.to_sym] = value
# Equals a single value
else
query_keys += "#{key} = :#{key} AND "
query_values[key.to_sym] = value
end
end
query_keys = query_keys[0..-5] # removes the last AND
query = [query_keys, query_values]
end
Upvotes: 0
Reputation: 663
You should use the default's ActiveRecord query functions, since they protect you from most of programmers' mistakes. Also, you should always verify the users' input before you pass it to DB queries.
The point about RoR and its Models is to abstract you from DB, making your app (almost) DB-agnostic.
By the way, this site may be helpful: http://rails-sqli.org/
EDIT: Another thing: never pass strings directly to Model's queries. Instead, just pass a hash. Example:
Don't use:
Model.where("id = #{params[id]}")
Instead use:
Model.where(id: params[id])
Upvotes: 3