Safe Rails Selects with Bound Parameters

Safe Rails Selects with Bound Parameters

Recently, we were trying to write a similarity(...) query in Postgres, and needed to pass in a parameter into a SELECT clause. Brakeman came back saying that we had a Possible SQL Injection. After looking at the code, we were directly interpolating the input into the SQL string, so it was absolutely right:

def self.with_similarity_to(cpu)
  select("similarity(cpu, #{cpu}) as similarity", '*')
end

Typically to solve this kind of problem, you can use ?s in your query and Rails will replace them automatically:

Book.where("title = ?", params[:title])

EXCEPT! The select method doesn't allow you to do this. Additional parameters passed to the select method are just enumerated in the select clause:

select("blah", "dad").to_sql # => SELECT blah, dad FROM...

So how do you escape input here so as to be safe from SQL injection?

Use ActiveRecord#sanitize_sql_array:

sanitize_sql_array(["name=? and group_id=?", "foo'bar", 4])
# => "name='foo''bar' and group_id=4"

This will make our statement safe (or at least, much safer than before) and we can move on!

def self.with_similarity_to(cpu)
  select(sanitize_sql_array(["similarity(cpu, ?) as similarity", cpu]), '*')
end

Any other pro tips on how to handle this? Let me know!