I have rails code that is consuming an oracle view/function.
This is my code:
def run_query
connection.exec_query(
"SELECT * FROM TABLE(FN_REQ(#{demo_type_param},#{demo_tid_param}}))")
end
When run Brakeman analyzer it warns of possible "sql injection attack"
I need to understand if this is a valid warning, if so, how do I remediate it?
Since this is a function & not an actual table, I am not sure what's the right way.
If it was a normal model, i would have just followed this pattern:
Model.where("mycolumn1= ? AND mycolumn2= ?", demo_type_param, demo_tid_param).first
Yes, it is real. Almost every time, you build any SQL query from simply concatenating variables, you are vulnerable to SQL injection. Generally, an SQL injection happens each time when data inserted into the query can look like valid SQL and can result in additional queries executed.
The only solution is to manually enforce appropriate escaping or to use prepared statements, with the latter being the preferred solution.
With ActiveRecord / Rails, you can use exec_query with binds directly
sql = 'SELECT * FROM TABLE(FN_REQ(?,?))'
connection.exec_query(sql, 'my query', [demo_type_param, demo_tid_param])
Here, Rails will prepare the statement on the database and add the parameters to it on execution, ensuring that everything is correctly escaped and save from SQL injection.
Related
My codebase has a potential vulnerability of SQL injection that I'd like to secure. I have a few queries in my codebase where I put variables in plain text, like this:
ActiveRecord::Base.connection.execute(
%{
SELECT ...
FROM ...
JOIN ...
WHERE ...
... model.foreign_id IN (#{array_of_ids})
GROUP BY 1;
}
)
I'm doing it like this because I don't think it's possible to use built-in ActiveRecord queries, due to the complexity of the query (there are ones more complex that we'd like to control).
My first thought was to use exec_query, but I've found I can't use that. I am using pgbouncer in transaction mode, so prepared_statements must remain off. Is there a way to execute a query with placeholders without preparing the function?
You could use the ActiveRecord sanitize methods
ActiveRecord::Base.sanitize_sql(['? IN ?', 'foreign_key', [1, ';true=true']])
=> "'model_name.foreign_key' IN 1,';true=true'"
https://github.com/rails/rails/blame/8642c564dab37366c2ca8950b428d1aec84eb10d/activerecord/lib/active_record/sanitization.rb
https://github.com/rails/rails/commit/9d43a84f73c1b3853a91d052a462ee60eccaf957
Is this safe from SQL injection:
Guest.where(:event_id => params[:id])
I am sending in params[:id] without doing any type of sanitization.
and in general, are all of those activerecord method safe? (like where, joins, etc..)
And if not, what is the best practise to be safe? Also, please is there any caveats/edge cases I should be aware of?
Thanks
All of ActiveRecord's query-building methods, like where, group, order, and so on, are safe against SQL injection AS LONG AS you do not pass them raw SQL strings. This is vulnerable to SQL injection:
Model.where("event_id = #{params[:id]}")
When you pass a string to a query-building method like that, the string will be inserted directly into the generated SQL query. This is useful sometimes, but it does raise the danger of an injection vulnerability. On the other hand, when you pass a hash of values, like this:
Model.where(event_id: params[:id])
...then AR automatically quotes the values for you, protecting you against SQL injection.
Yes, your code is safely being cleansed before it is run on the database. Rails protects you from sql injection by automatically sanitizing input.
THE EXCEPTION is string interpolation:
Guest.where("event_id = #{params[:id]}") # NEVER do this
Use one of these 2 options instead:
Guest.where(:event_id => params[:id]) # if you want pure ruby, use this
# OR
Guest.where("event_id = ?", params[:id]) # if you prefer raw SQL, use this
Check out the Rails Guide on security for more information related to sql injection as well as other common attacks.
If you really need to use raw sql, you could use quote to prevent SQL injection
Here is an example that has been copied from here
conn = ActiveRecord::Base.connection
name = conn.quote("John O'Neil")
title = conn.quote(nil)
query = "INSERT INTO users (name,title) VALUES (#{name}, #{title})"
conn.execute(query)
I need to receive an array of values of like:
['restaurant']
['restaurant', 'pharmacy']
I would like which approach to take to ensure that when I use this:
SELECT * FROM places WHERE type IN (array_joined_with_commas_and_quotes)
I don't get injection attacks.
I am writing the sentence without any library and I am working in Rails.
I don't have Active Record, I am doing a postgis query to an external server.
How about using the ActiveRecord functions for query building?
If you use rails, the gem should still be there.
Place.where(:type => ['foo', 'bar']).to_sql
You have two basic approaches (using ? for parameterization in these examples).
If this is an array, then your ['restaurant', 'pharmacy'] becomes '{"restaurant","pharmacy"}' and then you can:
SELECT * FROM places WHERE type = ANY (?);
You could also
SELECT * FROM places WHERE type IN (? ....);
Dynamically generating the number of parameters as you go. As scones mentioned ActiveRecord may be able to automate this for you. The point though is that these methods send data separately from the query, which means that the SQL cannot be injected into the query.
Suppose I've got a search box on a page in a Rails 3 app where you can search for a client by business name or city. In my controller's index method I do this:
if params[:search]
#clients = Client.where("clients.business_name LIKE :business_name OR clients.city = :city", :business_name => "%#{params[:search]}%", :city => params[:search])
Those hash values get substituted into the SQL and surrounded in quotes. If my input into the search box includes quotes or other dangerous characters, I'll see them being escaped in the development log, like:
...WHERE (clients.business_name LIKE '%Something\' DROP TABLE Foo%'...
Or
...WHERE... OR clients.city = 'Something OR 1=1')
So, since the OR 1=1 is inside the quotes Rails adds, it just produces no match for the city name, and since the quote in the DROP TABLE attempt is escaped, it also produces no match for the business name.
This isn't using actual prepared statements, where the query is sent to the database first without the search values filled in, then subsequently, the search values are sent to the database to fill in. I thought that was the safest approach, but Rails doesn't do it; I think this is because it's not available in all databases and implementations vary.
Is this open to SQL injection in some way? I don't see it, but again, it's not using prepared statements, so I wonder. If there's a vulnerability, how could I do this more safely?
No, there's not a SQL injection vulnerability here. ActiveRecord will call connection.quote on the values of the hash that you passed in as the second parameter to where, so you are safe.
The only potential SQL injection point I could think of would be if there were some undiscovered bug in connection.quote, which is pretty unlikely.
I have a bunch of SQL statements to execute on a database. (I'm doing things that Rails doesn't provide methods for, as far as I know: creating views, adding foreign keys, etc. It's mostly for non-Rails interaction with the data.) In essence, I'm doing the following:
sql = "statement_1; statement_2; statement_3; etc;"
ActiveRecord::Base.connection.execute(sql)
Or with newlines, like so:
sql = <<EOF
statement_1;
statement_2;
statement_3;
etc;
EOF
ActiveRecord::Base.connection.execute(sql)
(Obviously, these statements are just place holders, but I don't think their content matters, according to my tests.)
In either case, only the first statement is executed and the others seem to be ignored. Is that what's going on? I'm only seeing the effects of the first statement whenever I try more than one at a time. Do I need to execute each one separately? One set of statements is coming from a file, so it'd be nice to just load the contents of the file and execute. If there are better strategies I could adopt, I'd be interested in them.
I was hoping the documentation on execute would shed some light, but besides using the singular ("statement"), it doesn't. Perhaps it's because of the database engine I'm using? (For reference, I'm using SQLite at the moment.)
UPDATE: I ended up writing a method that does the following:
def extract_sql_statements(sql)
statements = []
sql.split(';').each do |statement|
statement.strip!
unless statement.empty?
statement += ';'
statements << statement
end
end
return statements
end
...and then looping over statements. It's fixed the problem, but if there are more elegant solutions, I would be interested in hearing about them.
If you look at the rails code then you will find that execute method runs the passed sql, so it should essentially run all your queries as long as they are ';' separated and valid.
EDIT: Sorry! No it won't because it will add ';' in between your query string and complain about wrong syntax