I am using ruby 1.8.7 and rails 2.3.2
The following code is prone to sql injection
params[:id] = "1) OR 1=1--"
User.delete_all("id = #{params[:id]}")
My question is by doing the following will be the best solution to avoid sql injection or not. If not then what is the best way to do so?
User.delete_all("id = #{params[:id].to_i}")
What about:
User.where(id: params[:id]).delete_all
Ok sorry for Rails 2.x its:
User.delete_all(["id = ?", params[:id]])
Check doc
Btw, be sure you want to use delete_all instead of destroy_all, the former doesn't trigger callbacks.
You can use this also
User.delete(params[:id])
The other answers answer this well for Rails and it'll work fine if you follow their suggestions. In a more generic setting when you have to handle this yourself you can typically use a regular expression to extract a value that's in an expected format. This is really simple with an integer id. Think of it like this:
if params[:id] =~ /(\d+)/
safe_id = $1.to_i
# do something with safe_id now
end
That gets a little more complicated when you're handling strings and arbitrary data. If you have to handle such data then you can use the quoting methods available for the database adapters. In Rails this is ultimately rolled into a consistent interface:
safe_string = ActiveRecord::Base.connection.quote(unsafe_string)
For most database systems this will handle single quotes and backslashes in a special manner.
If you're outside of Rails you will have to use the quoting methods specific to your database adapter, but usage is quite similar.
The takeaway:
If your data has a particular format, enforce the format with a regular expression
Otherwise, use your database adapter's quoting function to make the data "safe" for use in a query
Rails will handle most of this for you if you properly use the various methods and "conditions"
Use the rails methods to pass your where options. You can always hardcode them, as in the example that you give, but the usual way would be something like:
User.where(:id => params[:id]).delete_all
User.where("id = ?", params[:id]).delete_all
User.where("id = :id", :id => params[:id]).delete_all
They are well tested and in case a new vulnerability is detected, an update will fix the problem and your code will not need to be changed.
By the way, if you just want to delete 1 record based on its id, what I would do is:
User.find(params[:id]).destroy
Related
Am used to working with PHP and Prepared statement, now when i was looking at the following piece of code from rails ( since i a new to rails and Not sure about the syntax and stuff ) , i was wondering if the code is prone to SQLI injection
Code snippet (controller ) , param q is the value from a search box :
def index
query = %w(% %).join params[:q].to_s.gsub('%', '\\%').gsub('_', '\\_')
#posts = Post.where("name LIKE ? OR body LIKE ?", query, query).order(params[:order])
end
Thanks
What you have is intended to be safe. If it is not, then it's a bug in Rails.
.where accepts conditions in several formats. One is a raw string. If you build that string yourself, all bets are off and you are vulnerable.
As some recent documentation says:
Note that building your own string from user input may expose your
application to injection attacks if not done properly. As an
alternative, it is recommended to use one of the following methods.
In other words, ALL of the "following" (every other supported way) ways of doing things, are OK.
So if you are doing .where with anything other than string parameter, you should be fine.
As long as you don't interpolate within your where clause it should be safe. There are some good examples of SQL injection code here
I'm using Rails 3.2.11 on a Mac
I have this statement that works fine:
object= Object.find_by_id(params[:id])
I'm trying to add a condition to that statement so I did this:
object = Object.where("id = :id AND level <= :level",{:id => params[:id], :level => current_user.level})
Will there be any risk in this method? Any alternatives?
There's no risk presented by this statement, provided that ActiveRecord continues to uphold the contract of sanitizing input. An alternative would be a scope, but that's really just doing the same thing in a different syntax.
One thing you could do is set a default scope that defines the level restriction, then you could just do a standard find_by_id. But if that's undesirable, just use the syntax properly:
Object.where(id: params[:id], level: current_user.level)
There is no risk but the way it is defined is not easy to understand.
The simple statement will work:
object = Object.where("id = ? AND level <= ?",{params[:id], current_user.level})
as long as you let rails handle the sanitizing of your values, you'll have no issues. what i mean by this is run a sql command from rails using user input like
Object.where("id = #{params[:id]} AND level <= #{current_user.level}")
will be vulnerable to sql injection
I'm trying to update a field in using update_all. However I need the value to be taken from another field which is re-written to my specific format.
If I have something like this in my model:
def self.clean_mac_address()
clean_mac_address = :macaddress.gsub(/[^0-9a-z]/i, '')
end
When I run this:
Radacct.update_all("mac_clean = #{clean_mac_address}")
I get an error:
NoMethodError: undefined method `gsub' for :macaddress:Symbol
Any thoughts how I can do this? Or is there a simpler way to update the field?
update_all generates a single SQL query to run - it can't do clever stuff like change arbitrary bits of ruby into equivalent SQL.
You either need to load all you instances (via find_each for example) and fix them one by one (ie don't use update_all), for example
Foo.find_each do |foo|
# update foo here
foo.save!
end
Or find a way of expressing that cleaning operation in SQL. For example Postgres has a regexp_replace function
Foo.update_all("some_column = regexp_replace(some_column, 'your_regexp_here', '','g')")
Which would remove everything replacing that regexp. Obviously you'll need to check the documentation for your database to see whether it supports such a feature.
While the accepted answer provides a nice way to update_all, what I'd use is
read_with_clean_addr = Radacct.where(mac_clean: :macaddress.gsub(/[^0-9a-z]/i, ''))
read_with_clean_add.update_all(mac_clean: "#{clean_mac_address}")
I need to implement some search functionality within a Rails application. Most of the stuff I have found is generally aimed at simple plain-text search. I am trying to implement something much more specific. The sort of functionality I am looking to create is this (from a C application):
http://andyc.ac/query.gif
The form just submits the data entered by the user. So I need to translate strings like "3..7" into SQL conditions for the where method e.g.
TestLine.where( "test_int >= ? and test_int <= ?", MinInt, MaxInt )
It seems like this is something that already exists somewhere. The exact format expected is not too important, as the users are not shared between the Rails and C applications. How would this be done?
FWIW the specific functionality you describe is actually supported directly. Well.. almost. From the docs:
A range may be used in the hash to use the SQL BETWEEN operator:
Student.where(:grade => 9..12)
Of course then it's a matter of translating the user's string input to a Range, which isn't very complex, e.g.:
def str_to_range str
str =~ /(\d+)\.\.(\d+)/
Range.new *$~.captures.map(&:to_i)
end
It would probably make the most sense in a scope on your model. (Of course a shortcut would be to simply eval '9..12' but evaling input from the end user is a really, really bad idea.)
Give a look at thinking sphinx(http://freelancing-god.github.com/ts/en/). It might make your task a lot easier. You can search in that:
http://freelancing-god.github.com/ts/en/searching.html#basic
So I have a database of different code samples (read snippets).
The code samples are created by users.
Is there a way in Rails to execute it?
So for example I have the following code in my database (with id=123):
return #var.reverse
Is there a way for me to execute it? Something like:
#var = 'Hello'
#result = exec(CodeSample.find(123))
So the result would be 'olleH'
You can use eval:
code = '#var.reverse'
#var = 'Hello'
#result = eval(code) # => "olleH"
But be very careful in doing so; you're giving that code full access to your system. Try out eval('exit()') and see what happens.
To the eval answer (which is the right one) I would add: get thee a copy of the Pickaxe Book (either Programming Ruby or Programming Ruby 1.9 depending on your Ruby version) and read the chapter called "Locking Ruby in the Safe." That chapter is all about Ruby's safe levels and tainted objects, and the chapter opens with exactly your use case and why you need to be paranoid about it.
There is also another approach which you can use if you have a very limited use case or to limit the use cases.
I had to use this approach to allow users to dynamically specify relative times e.g.3.months.ago
I used a regex to sanitize the input from the users like so
PERMITTED_OPERATIONS = /^\{\%([1-9]\.(day|year|month|hour|minute)(s\.|\.)ago|Time\.now)\%\}$/
def permit?(operation)
return !PERMITTED_OPERATIONS.match(operation.to_s).nil?
end
You could extend the regex to allow for from_now as well or create an array of regexes for permitted operations and loop over it.
Would welcome any comments on this approach.