I've read about this for some time now on various SO threads, guides, etc... but all the answers are conflicting and contradictory.
It seems there's many similar methods, and a lot of the answers say to use a different one.
sanitize
sanitize_conditions
sanitize_sql
sanitize_sql_array
sanitize_sql_for_assignment
sanitize_sql_for_conditions
sanitize_sql_hash
sanitize_sql_hash_for_assignment
sanitize_sql_hash_for_conditions
sanitize_sql_like
I'm trying to write a 'raw query' adapter that lets me run raw Postgres queries, but allowing me to insert my own parameters that come from dangerous user input.
I can't use AR in these few instances because I'm doing complex lat/long calculations, aggregate functions, complex subqueries, etc.
So far I have tried 2 approaches:
Method 1
For this method, I don't know if sanitize
is the best option of the above, or if it will work in 100% of cases... (I'm using Postgres only)
class RawQuery
def exec(prepared, *params)
prepared = query.dup
params.flatten.each_with_index do |p, i|
prepared.gsub!("$#{i + 1}", ActiveRecord::Base.sanitize(p))
end
ActiveRecord::Base.connection.exec_query(prepared)
end
end
Trivial usage example (normally it wouldn't be this simple of course, or I would just use AR):
RawQuery.new.exec('SELECT * FROM users WHERE name = $1', params[:name])
Furthermore it seems that sanitize
delegates to quote
. But according to this SO post it says simply wrapping things with single quotes isn't secure... so I have no idea.
Method 2
I'm not sure if this is just as secure, but it seems to use an actual PG prepared function (which I assume is 100% secure). The only problem is rails doesn't print it out to the console, nor include the SQL execution time (which breaks my profiling tools).
class RawQuery
def prepare(query, *params)
name = "raw_query_#{SecureRandom.uuid.gsub('-', '')}"
connection = ActiveRecord::Base.connection.raw_connection
connection.prepare(name, query)
connection.exec_prepared(name, params)
end
end
Used the same way:
RawQuery.new.prepare('SELECT * FROM users WHERE name = $1', params[:name])
Is one method more secure over another? Are both 100% secure?
My apps always extend far outside of what Rails is capable of SQL-wise and I need a good lib I can include on all my projects which I know is completely safe.
sanitize
andsanitize_sql_for_conditions
? One other part I forgot to include in my original question is, the docs formethod sanitize_sql_for_conditions
mention: sanitizes them into a valid SQL fragment for a WHERE clause. The docs forsanitize
say Used to sanitize objects before they’re used in an SQL SELECT statement. Does that mean they're situation dependent, and one method can't be used anywhere in the SQL statement? (in the SELECT, WHERE, GROUP BY, etc). Or can I usesanitize
regardless of location? – Epanaphora